tags:

views:

3847

answers:

5

I've got code that looks like this:

for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
    bool isActive = (*i)->update();
    //if (!isActive) 
    //  items.remove(*i); 
    //else
       other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);

I'd like remove inactive items immediately after update them, inorder to avoid walking the list again. But if I add the commented-out lines, I get an error when I get to i++: "List iterator not incrementable". I tried some alternates which didn't increment in the for statement, but I couldn't get anything to work.

What's the best way to remove items as you are walking a std::list?

+15  A: 

You have to increment the iterator first (with i++) and then remove the previous element (e.g., by using the returned value from i++). You can change the code to a while loop like so:

std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
    bool isActive = (*i)->update();
    if (!isActive)
    {
        items.erase(i++);  // alternatively, i = items.erase(i);
    }
    else
    {
        other_code_involving(*i);
        ++i;
    }
}
Kristo
perfect, worked like a charm.
AShelly
Actually, that's not guaranteed to work. With "erase(i++);", we only know that the pre-incremented value is passed to erase(), and the i is incremented before the semi-colon, not necessarily before the call to erase(). "iterator prev = i++; erase(prev);" is sure to work, as is use the return value
James Curran
No James, i is incremented before calling erase, and the previous value is passed to the function. A function's arguments have to be fully evaluated before the function is called.
Brian Neal
@ James Curran: That's not correct. ALL arguments are fully evaluated before a function is called.
Martin York
Last time I checked, James was right. The only guarantees are that i++ will happen before the next statement and after i++ is used. Whether that is before erase(i++) is invoked or afterwards is compiler dependent. Otherwise constructs like foo.b(i++).c(i++) would be legal.
Mr.Ree
Martin York is correct. All arguments to a function call are fully evaluated before a function is called, no exceptions. That is simply how function work. And it has nothing to do with your foo.b(i++).c(i++) example (which is undefined in any case)
jalf
I stand corrected. See Kristo's question: http://stackoverflow.com/questions/598148/is-it-legal-to-use-the-increment-operator-in-a-c-function-call/599564
Mr.Ree
+10  A: 

You want to do:

i= items.erase(i);

That will correctly update the iterator to point to the location after the iterator you removed.

MSN
Be warned that you can't just drop that code into your for-loop. Otherwise you'll skip an element every time you remove one.
Kristo
+5  A: 

Use std::remove_if algorithm.

Edit: Work with collections should be like: 1. prepare collection. 2. process collection.

Life will be easier if you won't mix this steps.

  1. std::remove_if. or list::remove_if ( if you know that you work with list and not with the TCollection )
  2. std::for_each
Mykola Golubyev
std::list has a remove_if member function which is more efficient than the remove_if algorithm (and doesn't require the "remove-erase" idiom).
Brian Neal
+2  A: 

Removal invalidates only the iterators that point to the elements that are removed.

So in this case after removing *i , i is invalidated and you cannot do increment on it.

What you can do is first save the iterator of element that is to be removed , then increment the iterator and then remove the saved one.

Alien01
Using post-increment is far more elegant.
Brian Neal
+3  A: 
Mike
I did consider your SuperCool method, my hesitation was that the call to remove_if does not make it clear that the goal is to process the items, rather than remove them from the list of active ones. (The items aren't being deleted because they are only becoming inactive, not unneeded)
AShelly
I suppose you are right. On one hand I'm inclined to suggest changing the name of 'update' to remove the obscurity, but the truth is, this code is fancy with the functors, but it is also anything but non-obscure.
Mike