0

How do I remove element pointed to by iterator in a C++ list? why does not this work?

int main()
{
    list<int> l;
    l.push_back(5);
    l.push_back(6);

    
    for(auto &it: l)
    {
        l.erase(*it);
    }

    return 0;
}
JeJo
  • 30,635
  • 6
  • 49
  • 88
Deus Ex
  • 117
  • 1
  • 8
  • 1
    Deleting items from a `std::vector` while iterating over the items is bad. Lookup the documentation of [`std::vector::erase`](https://en.cppreference.com/w/cpp/container/vector/erase) and the sample code to understand how to use it safely. – R Sahu Jun 30 '20 at 04:33
  • 2
    Does this answer your question? [Can you remove elements from a std::list while iterating through it?](https://stackoverflow.com/questions/596162/can-you-remove-elements-from-a-stdlist-while-iterating-through-it) – lightBullet Jun 30 '20 at 04:36
  • 2
    There's also `clear` method to remove all elements from the list. – fas Jun 30 '20 at 04:37
  • 1
    Why does this not work: 1) In `for(auto &it: l)`, is an `int`, not an iterator, so `erase` won't accept it. B) if you did have an iterator, `erase`ing it in `list` Leaves you with an iterator to an item that's NOT in the list anymore. You can't use it to find the next item in the `list`. – user4581301 Jun 30 '20 at 04:56

4 Answers4

3

Why

for(auto &it: l){
    l.erase(*it);
}

fails to work:

it is not an iterator. In a range-based for loop, the variable declared before the colon, the range_declaration, will receive an item in the container, an int in this case. Since it will receive an int, auto will infer a type of int resulting in

for(int &it: l){
    l.erase(*it);
}

and std::list::erase requires an iterator. I'm assuming the * is simply the result of a bit of shotgun debugging to see if dereferencing what was believed to be an iterator helped (it wouldn't).

Side note: You cannot remove an item from a container while iterating the container with a range-based for loop. The magic in the background that implements the for loop looks something like

{
    auto cur = container.begin() ;
    auto end = container.end();
    for ( ; cur != end; ++cur) 
    {
        auto val = *cur;
        do_stuff 
    }
}

If in do_stuff you remove cur from the container, ++cur is invalid. Since cur's not in the container anymore, you can't use it to advance to the next item in the container. std::list is very permissive in its iterator invalidation rules. Many containers will fail when the cached end iterator is invalidated.

How to fix:

The given code appears to be trying to empty all the items in the list. std::list::clear does that for you with a single function call.

If you want to release a particular element or select elements by value, you should use std::list::remove or std::list::remove_if in conjunction with std::list::erase

eg:

l.erase(l.remove(5), l.end()); // remove all elements containing the number 5

if you want to remove the first item, std::list::pop_front. To remove the last item, std::list::pop_back. If you want to remove any other element by position, you must have a valid iterator for that position (If you do not already have one, see std::advance) and then call erase. Note that if you're having to iterate a lot to find items to remove, std::list may not be the right container for this job because list iteration is expensive and quickly eliminates the benefits of cheap insert and removal.

user4581301
  • 33,082
  • 7
  • 33
  • 54
0
int main()
{
    list<int> l;
    list<int>::iterator it;
    l.push_back(5);
    l.push_back(6);
    l.push_back(7);
    it=l.begin();// points to the first element
    l.erase(it);//pass the iterator to the erase method
    
    for(auto i=l.begin();i!=l.end();i++){
        cout<<*i<<" ";
    }

    return 0;
}

lets say you want to erase the first element. Then simply pass the iterator of the list to erase method.

0

If you wanted do it in loop cpprefenrence has nice example

when an element removed using erase method, it returns the next iterator to removed element, if last element end will return;

std::list<int> l{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

from above list, let's assume you wanted to remove 4 and 5;

below is the way;

std::list<int>::iterator first = l.begin();

std::advance( first, 4 );
auto it = l.erase( first ); // removes 4 and returns iterator to element 5
l.erase( it ) // removes 5;

as others suggested:

for(auto &it: l){ // range based loop, iterating through elements ex: 4, 5, 6
  //l.erase(*it);
  std::cout << it; // prints 4, 5, 6

}

you need below for loop to increment iterator

for( auto it = l.begin(); it != l.end(); it++) 
{
   // do something hear
}
pvc
  • 1,070
  • 1
  • 9
  • 14
-1

If you use an iterator-based loop, you can use the return value of erase to update the iterator:

  std::list<int> l = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

  for (auto it = l.begin();
       it != l.end();
       ++it)
  {
    if (*it % 3 == 0)
    {
      it = l.erase(it);
    }
  }
  for (auto i : l)
  {
    std::cout << i << std::endl;
  }
H Krishnan
  • 896
  • 9
  • 12