visualc++中与reverse_iterator相关的崩溃.是法律条文吗?

reverse_iterator related crashes in Visual C++. Is it legal code?

本文关键字:崩溃 法律条文 reverse 中与 iterator visualc++      更新时间:2023-10-16

我今天正在玩一些代码,并且非常惊讶地发现以下代码在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;
}

不合法。根据[lib.associative]在c++ 98中,"erase成员只能使迭代器和对被擦除元素的引用无效。"因为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

std::vector::erase(i)使指向"i"之前的vector元素的所有迭代器失效

MSDN参考