Vector erasure giving unexpected result

advertisements

Erase from vector giving different results with lambda function and function object.

I'm trying to delete 3rd element from a vector of strings. With function object 3rd and 6th element is getting deleted but with lambda version the code gives expected result.

I tried following code:

#include <iostream>
#include<algorithm>
#include<iterator>
#include<vector>

using namespace std;

int main()
{

vector<string> s;

copy(istream_iterator<string>(cin),
    istream_iterator<string>(),
    back_inserter(s));

    cout<<"S contains :"<<endl;
    for(auto x:s)
        cout<<x<<" ";
    cout<<endl;

#ifndef USE_LAMBDA    

struct Word_No{
int word_ith;
int word_count;

Word_No(int x)  :word_ith(x),word_count(0){}

bool operator () (string){
return ++word_count == word_ith;
}
};
//3rd Element remove
    s.erase(remove_if(s.begin(),s.end(),Word_No(3)),s.end());
#else
    int count =0;
    s.erase(remove_if(s.begin(),
                      s.end(),
                      [&count](string){
                         return ++count ==3; //3rd Element Remove
                            }),
                      s.end());
#endif

    cout<<"Now S contains :"<<endl;
    for(auto x:s)
        cout<<x<<" ";
}

Results:

g++ -o test test.cpp -std=gnu++0x

Input: King Queen Jack Ace Rook Knight Pawn Bishop

Output:

S contains : King Queen Jack Ace Rook Knight Pawn Bishop

Now S contains : King Queen Ace Rook Pawn Bishop //Wrong Result 3 and 6th element deleted.

g++ -o test test.cpp -std=gnu++0x -DUSE_LAMBDA

Input: King Queen Jack Ace Rook Knight Pawn Bishop

S contains : King Queen Jack Ace Rook Knight Pawn Bishop

Now S contains : King Queen Ace Rook Knight Pawn Bishop // Correct Result

Could anyone please explain these two behaviors ?


You're a victim of remove_if implementation, Josuttis's The C++ Standard Library describes this in detail.

The summary is:

The 3rd and 6th Element is removed for without lambda version because remove_if copies the predicate internally during processing.

It uses find_if internally to find the element that should be removed. Later the algorithm uses a copy of the predicate to process the remaining elements, if any, using remove_if_copy

In case of lambda, as you're passing argument by reference, lambda object internally used by remove_if share same state

This is the approximate implementation of remove_if

template<typename FwdItr, typename Pred>
FwdItr std::remove_if(FwdItr b, FwdItr e, Pred f)
{
    s = find_if(b,e,f);
    if(s==e)
        return b;
    else
     {
        FwdItr temp = b;
        return remove_copy_if(++temp, e, b, f);
     }
}