std::list erasing element while iterating

hio
Last edited on
This above is what stackoverflow told me to do in this situation,


Well that's incorrect. erase() invalidates the iterators so doing that won't help you as the iterator is already invalid.

erase() will return the next valid iterator so you need to get it like this:

iter = bl.erase(iter);
@firedraco
Well that's incorrect. erase() invalidates the iterators so doing that won't help you as the iterator is already invalid.


You are wrong. After the expression b1.erase(iter++); iter has a vvalid iterator because increment was done before the current element was deleted.

@xhtmlx

The problem is that there is two increments of the iterator in the loop. The first increment is done in the expression

b1.erase(iter++);

and the second increment is done in the iteration statement

for(iter = b1.begin(); iter != b1.end(); ++iter)

So for example if the iterator reference to the last element of the list after the first increment it will point to after the last element and then it will be increment one more that will result to memory violation.

So the loop shall be rewritten. It could be rewritten the following way


1
2
3
4
5
6
7
8
9
10
11
12
        for(iter = b1.begin(); iter != b1.end(); /* */ ) {
            (*iter).newDay();
            if((*iter).getAge() > 10) {
                iter = b1.erase(iter);
                std::cout << (*iter).getName() << " has died." << std::endl;
            }
            else {
                ++iter;
            }

            counter++;
        }



By the way I described the problem here http://cpp.forum24.ru/?1-3-40-00000003-000-0-0-1343478860

The thread is named "Why is not f( a++ ); and f( a ); a++ the same. But it is written in Russian. But I think google could help to translate the description.:)
Last edited on
vlad from moscow wrote:
You are wrong. After the expression b1.erase(iter++); iter has a vvalid iterator because increment was done before the current element was deleted.
Are you sure that the next iterator to the ereased one isn't invalided? Do you have a reference?

So for example if the iterator reference to the last element of the list after the first increment it will point to after the last element and then it will be increment one more that will result to memory violation.
Are you sure? Is it possible to get beyond the end by incrementing? Funnily I've never checked that (or does that behavior depend on implentation?)
@coder777
Are you sure that the next iterator to the ereased one isn't invalided? Do you have a reference?


From the section 23.3.5.4 list modifiers of the C++ Standard

iterator erase(const_iterator position);
iterator erase(const_iterator first, const_iterator last);
void pop_front();
void pop_back();
void clear() noexcept;
3 Effects: Invalidates only the iterators and references to the erased elements.



@coder777
Is it possible to get beyond the end by incrementing?


It is obvious because for example regular pointers may not be incremented past the element after the last element.
For example

int a[1] = {};
int *p = a;

++p; // valid; p now points after the last element of the array
++p; // invalid; p may not point past the element after the last element
Last edited on
An iterator is not the same as a pointer.
Considerer implementing std::list as circular with a header cell.

@ne555 (3373)

An iterator is not the same as a pointer.
Considerer implementing std::list as circular with a header cell.


You are wrong. An iterator is a common notion for entities including pointers that satisfy some requests A pointer is a random access iterator.
A square is a rectangle, but a rectangle isn't necessarily a square.

And you can get beyond the end while iterating, that's why you need iter != list.end(). In fact, list.end() is the position after the last valid position.
Last edited on
However there could exists an implementation that always gives you a valid cell (not a dereferenceable one).
So
1
2
3
for(std::list<T>::iterator it = list.begin(); true; ++it)
   if( it not_eq list.end() )
     std::cout << *it << std::endl;
will cycle the values forever.
Last edited on
@vlad from moscow

I'm still getting an access violation from the code you gave me. I'll read your article and see if I can solve it myself.

Image: http://i.imgur.com/GlYi2.png
Please show your updated code.
The sample from vlad from moscow must be changed to:
1
2
3
4
5
6
7
8
9
10
11
12
        for(iter = b1.begin(); iter != b1.end(); /* */ ) {
            (*iter).newDay();
            if((*iter).getAge() > 10) {
                std::cout << (*iter).getName() << " has died." << std::endl; // This must be done first! (otherwise it crashes when end() and it shows the wrong individual
                iter = b1.erase(iter);
            }
            else {
                ++iter;
            }

            counter++;
        }
@coder, It worked! Wow, I wish I would've thought of that. Thanks. :)
Last edited on
Topic archived. No new replies allowed.