Search code examples
c++c++11erasestdlist

How do I remove element pointed to by iterator in a C++ list?


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;
}

Solution

  • 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.