在使用iterator和pop_back进行循环时出现奇异的迭代器错误

Min*_*ine 5 c++ iterator c++11 reverse-iterator

给出下面的代码(说它的名字deque.cpp)

#include <cstdio>
#include <deque>

int main()
{
  std::deque<int> d = {1, 2, 3};
  for (auto it = d.rbegin(); it != d.rend();) {
    printf("it: %d\n", *it);
    ++it;
    d.pop_back();
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译g++ -std=c++11 -o deque deque.cpp,运行良好:

$ ./deque
it: 3
it: 2
it: 1
Run Code Online (Sandbox Code Playgroud)

但如果编译为-D_GLIBCXX_DEBUG(g++ -std=c++11 -o deque_debug deque.cpp -D_GLIBCXX_DEBUG,它得到以下错误:

$ ./deque_debug
it: 3
/usr/include/c++/4.8/debug/safe_iterator.h:171:error: attempt to copy-
    construct an iterator from a singular iterator.
...
Run Code Online (Sandbox Code Playgroud)

看起来第二个循环++it是从一个奇异的迭代器构造的.但我想在第一个循环之后++it,迭代器指向2,并且pop_back()不应该使它无效.那么为什么会出现错误呢?

注意:我知道代码可以重写如下:

  while (!d.empty()) {
    auto it = d.rbegin();
    printf("it: %d\n", *it);
    d.pop_back();
  }
Run Code Online (Sandbox Code Playgroud)

错误将消失.

但我确实想知道错误代码到底发生了什么.(这是否意味着反向迭代器的剂量实际上并不指向我期望的节点,而是它之后的节点?)


更新: @ Barry的回答解决了这个问题.请让我提出一个额外的相关问题:代码

  for (auto it = d.rbegin(); it != d.rend();) {
    printf("it: %d\n", *it);
    d.pop_back();
    ++it;   // <== moved below pop_back()
  }
Run Code Online (Sandbox Code Playgroud)

预计是错误的,++it应该在一个无效的迭代器上运行.但为什么代码不会导致错误?

Bar*_*rry 5

这里的问题来自反向迭代器实际上是什么.反向迭代器的相关关系是:

对于r从迭代器构造的反向迭代器i,关系&*r== &*(i-1)总是为真(只要r是可解除引用的); 因此,一个反向迭代器由一个过去的结束迭代器构造,引用序列中的最后一个元素.

当我们这样做时std::deque::pop_back(),我们无效:

迭代器和对擦除元素的引用无效.过去的迭代器也会失效.其他引用和迭代器不受影响.

rbegin()是由...构建的end().在我们it第一次递增之后,it将取消引用2但它的底层基础迭代器指向3- 这是擦除元素.所以引用它的迭代器包括你现在先进的反向迭代器.这就是为什么它失效了,这就是你看到你所看到的错误的原因.

反向迭代器很复杂.


it您可以将其重新分配给rbegin():而不是递增

for (auto it = d.rbegin(); it != d.rend();) {
    printf("it: %d\n", *it);
    d.pop_back();
    // 'it' and 'it+1' are both invalidated
    it = d.rbegin();
}
Run Code Online (Sandbox Code Playgroud)