与Visual C ++中的reverse_iterator相关的崩溃。这是合法代码吗?

时间:2011-12-14 05:06:48

标签: c++ visual-c++ gcc stl iterator

我今天正在玩一些代码,并且发现以下代码在Visual C ++中崩溃时非常惊讶。它看起来不错吗?当我运行代码时,gcc打印出1。英特尔也是如此。

#include <iostream>
#include <set>
#include <assert.h>

int main()
{
    std::set<int> sampleSet;
    sampleSet.insert(1);
    sampleSet.insert(2);
    sampleSet.insert(3);
    std::set<int>::iterator normalIt(sampleSet.begin());
    std::set<int>::reverse_iterator reverseIt(sampleSet.rbegin());

    ++normalIt;
    ++reverseIt;

    int test1(*reverseIt); // 2
    assert(*normalIt == *reverseIt); //they're both = 2
    std::set<int>::iterator gonnaDelete((++reverseIt).base()); // gonnaDelete points to 2
    int test2(*reverseIt); // 1
    sampleSet.erase(gonnaDelete);
    int test3(*reverseIt); // 1???   Visual Studio 2010 crashes here.... gcc is fine, but not sure if this is legal
    std::cout << test3 << std::endl;

    return 0;
}

2 个答案:

答案 0 :(得分:1)

std :: vector :: erase(i)在“i”

之前引用任何引用vector元素的迭代器无效

MSDN Reference

答案 1 :(得分:1)

这不合法。根据C ++ 98中的[lib.associative.reqmts],“擦除成员只能使迭代器和对擦除元素的引用无效。”因为reverse_iterator的定义类似于:

template<typename Iter>
class reverse_iterator {
  Iter current;
  Iter tmp;
 public:
  ...
  Iter base() { return current; }
  reference operator*() {
    tmp = current;
    --tmp;
    return *tmp;
  }
};

当您删除gonnaDelete时,您也会使reverseIt.current无效,因此当您随后取消引用reverseIt时,您将获得未定义的行为。

现在,set迭代器往往是指向节点的简单指针,而优化的分配器有时会单独留下已删除节点的内存,因此即使在使其中一个失效之后,代码有时也会按照您的意愿执行,但它是根本没有保证工作。我怀疑VC ++默认启用调试模式来捕捉这种错误。要启用gcc的等效模式,请尝试使用-D_GLIBCXX_DEBUG构建:http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/manual/manual/bk01pt03ch17s03.html