std::list unique

M

Mihai Vasilian

Hi,
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.
 
V

Victor Bazarov

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).

How do you gather?
Is there something in the standard about this?

An iterator becomes invalid if you erase from the collection the element
to which the iterator points by means of 'remove'. See [list.ops]/15.
Since you've removed all elements with value '3', any iterators that
might have been obtained to point to an element with that value *are
invalid* after that operation.

V
 
S

SG

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top