Frequent error while using std::erase with std::remove_if in C++

To remove an element from container of type vector, string, list or deque we use erase() method with an iterator to unneeded element as a parameter.

After some time we do optimize the code or start to use STL algorithm remove_if() from the very beginning. Next we add some fancy boost::bind stuff for convenience and etc.

And once in a while we have to remove several elements from container instead of one. And it does not matter if it happens in new code, or while extending old. And here many C++ beginners can do the mistake, either but not knowing about it or just lazy enough not to check the erase() method documentation. They copy-paste the old code and change only remove_if() search condition. But they forget, that if only one argument is passed to erase(), it will delete that only element which is that iterator argument pointing to.

If several elements are needed to be removed from the container, there is an overloaded erase() method which accepts two iterators , to the start and to the end of the sequence to be removed. When remove_if() algorithm is used, it rearranges elements that should not be removed to the beginning of the container, leaving some trash in the end. For example, let’s have a vector , containing the following values:
1 2 3 1 2 3 1 2 3

Then if we call
bool IsTwo (int i) { return (i == 2); }

tstart = remove_if(v.begin(), v.end(), isTwo)
//remove elements with value 2 from the container v

we’ll get
1 3 1 3 1 3 ? ? ?
where ? – is some trash. tstart iterator, returned by remove_if(), will point to the first trash element, the third from the end in our case. Trash may contain the same values as before calling remove_if(), but that’s not guaranteed. Container size remains unchanged.

So if we write down the whole code which novice works with, we’ll get the following:
v.erase(remove_if(v.begin(), v.end(), isTwo));

And erase() will remove only the first trash element from the container and will change containers size by one. The contents will be the following:
1 3 1 3 1 3 ? ?
which is obviously wrong and causes unpredictable consequences if working further with this container.

How to avoid that? When developer is not sure in his skills, he is advised to check the documentation, and find out, that if we call erase() as follows:
v.erase(remove_if(v.begin(), v.end(), isTwo), v.end());
all the trash element will be removed from the container.

If the developer (sometimes overly) confident, the tests are always to the rescue. And don’t be lazy, write tests for border cases as well. In our case we should at least check removing not only single element, but several as well, and don’t forget to test none elements removal, and even test with empty container.

Leave a Reply

Your email address will not be published. Required fields are marked *