A Big Ball Of Lettuce
A Big Ball Of Lettuce

Reputation: 313

Erasing an element from a list container

I am having difficulty understanding why the code is behaving this way. First of all I have read the relevant answered material and still found the explanations abit advanced. So I'm wondering if some-one could explain this in a simple fashion.

Ok, so I am erasing elements from a list.

The list contains int elements that are both odd and even numbers. This part I understand. Here is the code I originally wrote to remove the odd numbers from the list

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); i++)
    {
        if(*i%2 == 0 )
        {
             lNo.erase(i);
        }
        else
        {
             cout << " " << *i;
        }
     }

With this code, the program simply does not compile, and I read a message stating that the program has to shut down.

The erase function works when I write this code:

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); i++)
    {
        if(*i%2 == 0 )
        {
             i = lNo.erase(i);  
        }
        else
        {
            cout << " " << *i;
        }
   }

I just need to uderstand why the program works when I code i = lNo.erase(i) and not with just lNo.erase(i)?

A simple concise answer would be much appreciated. I know that different containers have different constraints, so which constraint did I violate with the original piece of code?.

Upvotes: 3

Views: 136

Answers (3)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361302

Even your second code is incorrect.

The correct code should be this:

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); /*NOTHING HERE*/ )
{
    if(*i%2 == 0 )
    {
         i = lNo.erase(i);  
    }
    else
    {
        cout << " " << *i;
        ++i; //INCREMENT HERE, not in the for loop
    }
}

Note that erase() erases the item and returns the iterator to the next item. That means, you don't need to increment i in your code when you erase; instead you just need to update i with the returned value from erase.

You could use erase-remove idiom as:

lNo.erase(std::remove_if(lNo.begin(),
                         lNo.end(),
                         [](int i) { return i%2 == 0; }), 
                         lNo.end());

Live demo

Upvotes: 5

OOEngineer
OOEngineer

Reputation: 447

The thing is that you're using an iterator that doesn't expect the chaining of your list to be modified. So when you're calling erase() on your list, the chaining is effectively modified and so your iterator isn't valid anymore. The i++ statement doesn't work anymore.

But, in the 2nd version, you re-assign your iterator to valid object that still have the chaining intact, so the i++ statement can still work.

In some framework, you have 2 kinds of iterators, the kind that do reflect immediately what's happening to the underlying dataset (here is what you're using), and the kind that doesn't change their chaining whatever happening to the underlying dataset (so you don't have to use the weird trick of the 2nd version).

Upvotes: 0

R. Martinho Fernandes
R. Martinho Fernandes

Reputation: 234384

As stated in the documentation, the erase function invalidates the iterator passed in. That means it cannot be used again. The loop cannot proceed with that iterator.

The documentation also states that it returns an iterator to the element that was after the erased one. That iterator is valid and can be used to proceed.

Note however that since it returns an iterator to the element after the one that was erased, there is no need to increment that to advance, or that element will not be checked for oddness. The loop should catter for that and only increment when no erasure was done.

Upvotes: 6

Related Questions