erase() after performing remove_if()
Asked Answered
K

2

13

I've created a function to run through a vector of strings and remove any strings of length 3 or less. This is a lesson in using the STL Algorithm library.

I'm having trouble in that the functions work but not only does it delete strings of length 3 or less but it also appends the string "vector" to the end.

The output should be

This test vector

and instead it is

This test vector vector"

How can I fix it?

/*
* using remove_if and custom call back function, write RemoveShortWords 
* that accepts a vector<string> and removes all strings of length 3 or
* less from it. *shoot for 2 lines of code in functions.
*/

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;

bool StringLengthTest(string test) //test condition for remove_if algo.  
{
    return test.length() <= 3;
}

void RemoveShortWords(vector<string> &myVector)
{
    //erase anything in vector with length <= 3
    myVector.erase(remove_if(myVector.begin(),
                             myVector.end(),
                             StringLengthTest));
}

int main ()
{
    //add some strings to vector
    vector<string> myVector;
    myVector.push_back("This");
    myVector.push_back("is");
    myVector.push_back("a");
    myVector.push_back("test");
    myVector.push_back("vector");

    //print out contents of myVector (debugging)
    copy(myVector.begin(), myVector.end(), ostream_iterator<string>(cout," "));
    cout << endl; //flush the stream

    RemoveShortWords(myVector); //remove words with length <= 3

    //print out myVector (debugging)
    copy(myVector.begin(), myVector.end(), ostream_iterator<string>(cout," "));
    cout << endl;

    system("pause");
    return 0;
}
Kielty answered 29/1, 2012 at 14:23 Comment(0)
P
31

It is easiest to understand this if you seperate the statements:

auto iter(remove_if(myVector.begin(), myVector.end(), StringLengthTest));
myVector.erase(iter);

These 2 lines do the same as your single line. And it should be clear now what the "bug" is. remove_if, works first. It iterates over the whole vector and moves all "selected" entries "to the end" (better said: it moves the non selected entries to the front). After it has run it returns an iterator to the "last" position of the left over entries, something like:

this
test
vector
test <- iterator points here
vector

Then you run erase with a single iterator. That means you erase a single element pointed at - so you erase the "test" element. - What is left over is what you are seeing.

To fix it simply erase from the vector returned by remove_if to the end().:

myVector.erase(remove_if(myVector.begin(), myVector.end(), StringLengthTest), myVector.end()); //erase anything in vector with length <= 3
Pyrotechnics answered 29/1, 2012 at 14:32 Comment(2)
Great details. Thank you so much for clarify what is going on!Kielty
This would bite even more if myVector were empty. Then iter would equal myVector.end(), and erasing using erase(iter) would lead to UB.Montanez
B
12

You should be using the two parameter form of erase:

myVector.erase(remove_if(myVector.begin(), myVector.end(), StringLengthTest),
               myVector.end());
Buckjumper answered 29/1, 2012 at 14:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.