8

I want to use the new C++11 for each loop to iterate over all elements of a list and erase certains elements. For example

std::list<int> myList;
myList.push_back(1); 
myList.push_back(13);
myList.push_back(9);
myList.push_back(4);

for(int element : myList) {
    if(element > 5) {
        //Do something with the element

        //erase the element
    }else{
        //Do something else with the element
    }
}

Is it possible to do this using the for each loop or do I have to go back to iterators to achive this?

Haatschii
  • 9,021
  • 10
  • 58
  • 95
  • 3
    why cant you use remove_if/erase? – Karthik T Jan 13 '13 at 15:34
  • 3
    Or just `list::remove_if`, no iterators necessary. – Benjamin Lindley Jan 13 '13 at 15:36
  • @KarthikT and @BenjaminLindley: Sorry, I didn't mention in my question. I want to do some stuff with the elements that satisfy the condition and also with all others. I probably could put this into to Predicate function used by `list::remove_if`, but I find that is not very nice. – Haatschii Jan 13 '13 at 15:40
  • 1
    @Haatschii depending on the nature of the processing, if it isnt too performance critical, I would do it in 2 passes, cleaner that way. – Karthik T Jan 13 '13 at 15:45

3 Answers3

6

You should be able to just do this

myList.erase(std::remove_if(myList.begin(), myList.end(),
    [](int& element) 
    { 
        return element > 5;
    } 
    ),myList.end());

or simply (courtesy Benjamin Lindley)

myList.remove_if(
    [](int& element) 
    { 
        return element > 5;
    } 
    );
Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • 2
    shifting elements is pretty inefficient for a list – Cheers and hth. - Alf Jan 13 '13 at 15:47
  • hm, nice edit :) but wait... `std::erase`? have you tried to *compile* this? – Cheers and hth. - Alf Jan 13 '13 at 15:54
  • 2
    @Cheersandhth.-Alf I believe you are mistaken, removal and inserting is O(1) for std::list. Shifting will happen for sequential containers such as std::vector. – Tamás Szelei Jan 13 '13 at 15:55
  • 4
    @fish: `std::remove_if` doesn't remove elements, it moves or swaps them (aka shifting). – Ben Voigt Jan 13 '13 at 16:02
  • @fish: you might just try it, removing the call to (non-existent) `std::erase`. it would have been a good idea to try it before commenting. – Cheers and hth. - Alf Jan 13 '13 at 16:04
  • Using a lambda function, it is not as ugly as I thought to use `remove_if`. Important to note, that I need to use `(int& element)` in order to be able to change the elements (and also prevent copys when using objects instead of ints), as Matthieu's answer points out. I think I'll go this way. Thanks – Haatschii Jan 13 '13 at 16:05
5

You can't erase elements of standard containers in a range-based for loop over that container -- the loop itself has an iterator to the element that you're currently visiting, and erasing it would invalidate that iterator before the loop increments it.

Range-based for is defined in 6.5.4 of the standard to be equivalent to (slightly simplified):

for (auto __begin=begin-expr, __end=end-expr; __begin != __end; ++__begin) {
    for-range-declaration = *__begin;
    statement
}

begin-expr and end-expr have their own lengthy definition, but in your example they are myList.begin() and myList.end() respectively.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
0

Nope, I don't think so. See this SO answer:

No, you can't. Range-based for is for when you need to access each element of a container once.

You should use the normal for loop or one of it's cousins if you need to modify the container as you go along, access an element more than once, or otherwise iterate in a non-linear fashion through the container.

Community
  • 1
  • 1
Michael Rawson
  • 1,905
  • 1
  • 15
  • 25