tags:

views:

323

answers:

3

Say we've got:

struct IsEven {
   bool operator() (int i) { return i % 2 == 0; }
};

Then:

vector<int> V; // fill with ints
vector<int>::iterator new_end = remove_if(V.begin(), V.end(), IsEven());
V.erase(new_end, V.end());

works fine (it leaves V with only the odd integers). But it seems that the elements from new_end to V.end() are not the even integers that we're deleting. For example, if v starts out as 1 4 2 8 5 7, then I'm getting 8 5 7 for those elements (although after the erase call, the vector indeed has 1 5 7 left).

Apparently, (according to http://www.sgi.com/tech/stl/remove_if.html)

The iterators in the range [new_last, last) are all still dereferenceable,
but the elements that they point to are unspecified.

First of all, WTF? And second, how do I get around this without essentially reimplementing remove_if?

+2  A: 

I suppose the point is that the function is called remove_if for a reason. It removes elements. It doesn't move them or select them. After you've called remove_if, you're no longer guaranteed that the elements you removed exist. All you're guaranteed is that the elements between first and new_last do not contain any of the removed elements.

std::partition would be a better choice, wouldn't it? Or perhaps remove_copy_if, depending on exactly what you're trying to do.

jalf
+6  A: 

It sounds like you want to use partition() to partition the vector into groups of odd values at the start and even values at the end. partition() will return an iterator to the first element of the second grouping.

As for the WTF, I'm not sure why you would expect a remove operation to preserve the elements you want to remove by copying them (that's extra work) to the end of the container. Most people consider the WTF in remove() (and it's cousins) to be the fact that the size of the vector is not reduced and you have to call erase() to actually delete undesired elements after the remove operation.

Michael Burr
That actually was my initial WTF, but I've gotten used to it, and I didn't realize that partition() existed.
Jesse Beder
It makes sense when you think about it though. An iterator only represents a sequence within a container, not the container itself.It may not represent every element in the container, or data in a container at all.But yes, it often trips newbies up. And then they have an a-ha moment and *get* STL
jalf
jalf is right. It's confusing initially, but if remove_if() was able to get rid of the remaining elements itself, that would mean that it could not work with arrays. (How do you "delete elements from" an array?)
j_random_hacker
Yeah, I get that. I just assumed remove_if did what partition actually does. My problem was that the SGI site doesn't reference partition (from the remove_if page) so I had no way of finding it (or knowing that such a thing exists).
Jesse Beder
+2  A: 

If you really want to use the "removed" elements, you want std::partition.

Roger Lipscombe