I have this code:
#include <iostream>
#include <list>
int main()
{
typedef std::list<int> list;
int i0t[5]={-1, 2, 3, 3, 5};
list list_1(i0t, i0t+5);
list::reverse_iterator ri0 = ++list_1.rbegin();
list_1.unique();
list_1.remove(3);
int val = *ri0; // why is this valid ?
std::cout << "val = " << val << "\n";
return 0;
}
My intuition was that ri0 iterator would become invalid after
list_1.unique();
list_1.remove(3);
but it didn't happen (I guess).
Is there something in the standard about this?
Thank you.
That's actually an interesting test case. First I was going to mention
things such as "undefined behaviour" and that you cannot rely on any
kind of behaviour when you do something that invokes undefined
behaviour. Then I was going to tell you about the fact that the
compiler vendors of popular C++ compilers offer standard library modes
that are slower but do lots of additional checking (that is not
required by the C++ ISO standard but obviously helps development).
Then I wanted to show you what kind of error you get using G++'s
stdlib debug mode. But there is actually none. Even with all debug
modes turned on I get the result:
val = 2
That made me think.
It turns out that your program DOES NOT invoke undefined behaviour.
The C++ standard defines list<T>::reverse_iterator to be a
std::reverse_iterator<list<T>::iterator>. Internally your ri0
reverse_iterator contains a list<T>::iterator that actually points to
the last element with the value 5. It works like this:
template<class Iter>
class reverse_iterator {
Iter it;
public:
...
reference operator*() const {
Iter temp = it;
--temp;
return *temp;
}
...
};
so that when such an object stores the iterator list<T>::end() it
logically points to the last element and when such an object stores
the iterator list<T>::begin() it logically points to beyond the first
element.
If I were to repeat the test with the "normal" iterator pointing to
the last 3:
#include <iostream>
#include <list>
int main()
{
typedef std::list<int> list;
int i0t[5]={-1, 2, 3, 3, 5};
list list_1(i0t, i0t+5);
list::iterator i = list_1.end();
--i;
--i; // now points to the 2nd 3
list_1.unique();
list_1.remove(3);
// iterator i now invalid
int val = *i; // this invokes undefined behaviour
std::cout << "val = " << val << "\n";
return 0;
}
I get the following runtime behaviour (with all debug modes turned
on):
.../4.6.1/include/c++/debug/safe_iterator.h:193:
error: attempt to dereference a singular iterator.
Objects involved in the operation:
iterator "this" @ 0x000000000022FD30 {
type =
N11__gnu_debug14_Safe_iteratorINSt9__cxx199814_List_iteratorIiEENSt7__deb
ug4listIiSaIiEEEEE (mutable iterator);
state = singular;
references sequence with type `NSt7__debug4listIiSaIiEEE' @
0x000000000022FD30
}
and when run in a debugger it breaks automatically in this situation
so that I can inspect the stack etc ...
Again: The C++ ISO standard does not define any behaviour in this
situation. You get this checking only as an additional feature of your
C++ implementation.
Cheers!
SG