Giving the below code (say it's named 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;
}
Compile with g++ -std=c++11 -o deque deque.cpp
, it runs well:
$ ./deque
it: 3
it: 2
it: 1
But if compile with -D_GLIBCXX_DEBUG
(g++ -std=c++11 -o deque_debug deque.cpp -D_GLIBCXX_DEBUG
, it gets below error:
$ ./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.
...
It looks like the second loop's ++it
is constructing from an singular iterator.
But I think after the first loop's ++it
, the iterator points to 2, and pop_back()
should not invalidate it. Then why the error occurs?
Note: I know the code could be re-write as below:
while (!d.empty()) {
auto it = d.rbegin();
printf("it: %d\n", *it);
d.pop_back();
}
And the error will be gone.
But I do want to know what exactly happens on the error code. (Does it mean the reverse iterator dose not actually point to the node I expect, but the one after it?)
Update: @Barry's answer resolved the question. Please let me put an extra related question: the code
for (auto it = d.rbegin(); it != d.rend();) {
printf("it: %d\n", *it);
d.pop_back();
++it; // <== moved below pop_back()
}
is expected to be wrong, where ++it
should be operating on an invalidated iterator. But why the code does not cause error?
The problem here arises from what reverse iterators actually are. The relevant relationship for a reverse iterator is:
For a reverse iterator
r
constructed from an iteratori
, the relationship&*r
==&*(i-1)
is always true (as long as r is dereferenceable); thus a reverse iterator constructed from a one-past-the-end iterator dereferences to the last element in a sequence.
When we then do std::deque::pop_back()
, we invalidate:
Iterators and references to the erased element are invalidated. The past-the-end iterator is also invalidated. Other references and iterators are not affected.
rbegin()
is constructed from end()
. After we increment it
the first time, it
will dereference to the 2
but its underlying base iterator is pointing to the 3
- that's the erased element. So the iterators referring to it include your now-advanced reverse iterator. That's why it's invalidated and that's why you're seeing the error that you're seeing.
Reverse iterators are complicated.
Instead of incrementing it
, you could reassign it to 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();
}