Search code examples
c++vectorerase-remove-idiom

having trouble with vector.erase() and remove_if()


Prior to using remove_if, I was using remove. So say if I had vec = {1, 2, 2, 2, 5}, and wanted to remove all 2s, I would do:

for (vector<int>::iterator it = vec.begin(); it!= vector.end(); ++it){
    if (*it == 2){
       vec.erase(remove(vec.begin(),vec.end(), *it), vec.end());
    }
}

This will not work as it's not possible and illogical to iterate through a vector as deletion is being done.

So then I discovered remove_if(). However, for some reason I can't get it to work within the context of a class. Here's my code:

class SomeClass{
    private:
       vector<int> vec;
    public:
       SomeClass(){
           //initalizae vec to {1,2,2,2,4,5,6,8}
       }
       bool is_even(int value){
           return value % 2 == 0;
       }
       void delete(int a){
           vec.erase(remove_if(vec.begin(), vec.end(), a), vec.end());
       }
       void delete_even(int a){
           vec.erase(remove_if(vec.begin(), vec.end(), this->is_even(a)), vec.end());
       }

I am guessing void delete will not work because a is an int and I need a bool value, but not sure how to express "if int a is in this vector return true" as the third parameter for remove_if. And I'd expect void delete_even to work but I get

note: in instantiation of function template specialization 'std::__1::remove_if<std::__1::__wrap_iter<int *>, bool>' requested here

Solution

  • Just use std::remove to remove an int value from the collection, without searching for it yourself:

    vec.erase(std::remove(vec.begin(), vec.end(), 2), vec.end());
    

    This will remove all occurrences of 2 from vec.

    The usage of remove_if is the same, only then rather than an element you give a predicate (typically a function); if C++11 is available to you, you can use it with a lambda as such:

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

    If you want to use is_even as a predicate to remove_if (or another member function of your class), then as mentioned in the comments, you should make it a static member function, preferably. You can also bind to non-static members, but there's no real reason to do that here; but see Using std::bind with member function, use object pointer or not for this argument? if you're curious, the question itself contains the correct syntax. A sample for is_even as static:

     static bool is_even(int value) {
         return value % 2 == 0;
     }
    
     void delete_even(int a){
         vec.erase(remove_if(vec.begin(), vec.end(), is_even), vec.end());
     }