1

Possible Duplicate:
How to filter items from a std::map?
std::list::erase not working

I have some silly questions regarding remove, erase in std::list.

I have a class defined as:

class CBase
{
public:
    CBase(int i): m(i)
    {};

    int m;
};

then, I set it up as:

list<CBase> ml;
CBase b1(1);
CBase b2(2);
CBase b3(3);
CBase b4(4);
ml.push_back(b1);
ml.push_back(b2);
ml.push_back(b3);
ml.push_back(b4);

I can erase the item which has m==2 by;

for (list<CBase>::iterator it=ml.begin(); it!=ml.end(); ++it)
{
    if (it->m == 2)
    {
        ml.erase(it--);
    }
}
    // show what we have now:
for (list<CBase>::iterator it=ml.begin(); it!=ml.end(); it++)
{
    cout << it->m;
}

But if I do:

for (list<CBase>::iterator it=ml.begin(); it!=ml.end(); it++)
{
    if (it->m == 2)
    {
        ml.erase(it);
        it--;
    }
}

There will be exception. Why is this?

And if I want to remove b3,

ml.remove(b3);

will not compile. All the examples I found online use list<int>, and there is no problem calling mylist.remove(3), if mylist is list<int>. How can I make it work?

Community
  • 1
  • 1
user1866880
  • 1,437
  • 6
  • 18
  • 26
  • dup: http://stackoverflow.com/questions/12666869/stdlisterase-not-working – Csq Jan 21 '13 at 14:55
  • The STL has very efficient algorithms just for cases like this anyways: http://en.cppreference.com/w/cpp/algorithm/remove – SirDarius Jan 21 '13 at 14:58
  • 1
    @SirDarius: `std::list` has its own remove/remove_if functions, which are more efficient for it because they rearrange links, rather than moving elements. – Benjamin Lindley Jan 21 '13 at 14:59
  • @BenjaminLindley totally true, I just found out about how std::remove really worked and was about to edit my comment to refer to those specific methods – SirDarius Jan 21 '13 at 15:01

3 Answers3

2

Your are dereferencing iterator pointing at the erased element. Use the return value of the erase() member function:

it = ml.erase(it);
// 'it' now points at first element after the last deleted element
wilx
  • 17,697
  • 6
  • 59
  • 114
1

Because erase invalidates the iterator. It may no longer be used, including the decrement operator.

EDIT: as for remove, it removes elements with value equal to the one you specify. std::list uses the operator== for the comparison and unless you have it defined, compilation will fail. Simply define the operator and should be OK.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
  • In some cases, perhaps not this one, defining an `operator==` for a class doesn't really make sense, or it doesn't have one obviously correct implementation, and shouldn't be done just for the sake of being able to be used with certain algorithms when the object is placed in a container. If that's the case, you can use `std::list<>::remove_if`, passing it an appropriate predicate. – Benjamin Lindley Jan 21 '13 at 15:06
  • Well, I simply compiled his code and observed the compiler error. I thought in stl it is standard to use the `operator<` for comparison, but it seems this is not the case here. – Ivaylo Strandjev Jan 21 '13 at 15:16
  • Don't worry, I wasn't saying your suggestion was a bad idea. I was just offering a little extra information so that he's not stuck when he wants to use this technique on another class where perhaps `operator==` is not applicable. – Benjamin Lindley Jan 21 '13 at 15:20
1

After the erase, the iterator you passed to it will be invalid.

Now, using

ml.erase(it--);

you're passing erase a copy of your iterator, and moving your copy backwards so it no longer refers to the same place in the list.
The -- happens after erase's copy has been prepared, but before erase is actually called.
After the call, your iterator is still valid, and it's one position before the element you removed.

But if you do

ml.erase(it);
it--;

it is still trying to reference the deleted element after the call, and you get an exception when you try to modify it because it's invalid.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82