Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Erasing item in a for(-each) auto loop

Is there a way to erase specific elements when using a auto variable in a for loop like this?

for(auto a: m_Connections)
{
    if(something)
    {
        //Erase this element

    }
}

I know I can either do say

for(auto it=m_map.begin() ...

or

for(map<int,int>::iterator it=m_map.begin() ...

and manually increment the iterator (and erase) but if I could do it with less lines of code I'd be happier.

Thanks!

like image 487
Valmond Avatar asked Oct 23 '14 09:10

Valmond


2 Answers

You can't. A range-based loop makes a simple iteration over a range simpler, but doesn't support anything that invalidates either the range, or the iterator it uses. Of course, even if that were supported, you couldn't efficiently erase an element without access to the iterator.

You'll need an old-school loop, along the lines of

for (auto it = container.begin(); it != container.end();) {
    if (something) {
        it = container.erase(it);
    } else {
        ++it;
    }
}

or a combination of container.erase() and std::remove_if, if you like that sort of thing.

like image 132
Mike Seymour Avatar answered Oct 01 '22 20:10

Mike Seymour


No, there isn't. Range based for loop is used to access each element of a container once.

Every time an element is removed from the container, iterators at or after the erased element are no longer valid (and given the implementation of the range-based-for this is a problem).

You should use the normal for loop (or a while) if you need to modify the container as you go along.

If you want to erase elements for which a predicate returns true, a good way is:

m_Connections.erase(
  std::remove_if(m_Connections.begin(),
                 m_Connections.end(),
                 [](Type elem) { return predicate(elem); }),
  m_Connections.end());

std::remove_if doesn't mix iteration logic with the predicate.

like image 20
manlio Avatar answered Oct 01 '22 19:10

manlio