std::remove_if not working properly [duplicate]

2019-07-15 10:07发布

问题:

This question already has an answer here:

  • Erasing elements from a vector 4 answers

Here my code. I want remove from vector all elements with successfully called method 'release'.

bool foo::release()
{
    return true;
}

// ...
vector<foo> vec;
// ...
remove_if(vec.begin(), vec.end(), [](foo & f) { return f.release() == true; });
// ...

But remove_if not deleting all elements from vector vec. How remove_if works?

回答1:

std::remove_if re-arranges the elements of the vector such that the elements you want to keep are in the range [vec.begin(), return_iterator) (note the partially open range). So you need to call std::vector::erase to make sure the vector contains only the desired elements. This is called the erase-remove idiom:

auto it = remove_if(vec.begin(),
                    vec.end(),
                    [](foo & f) { return f.release() == true; });

vec.erase(it, vec.end());

Here, I have split it into two lines for clarity, but it is often seen as a one-liner.



回答2:

Because the remove_if algorithm operates on a range of elements denoted by two forward iterators, it has no knowledge of the underlying container or collection.

Thus, no elements are actually removed from the container. Rather, all elements which don't fit the remove criteria are brought together to the front of the range, in the same relative order.

The remaining elements are left in a valid, but unspecified, state. When this is done, remove returns an iterator pointing one element past the last unremoved element.

To actually eliminate elements from the container, remove should be combined with the container's erase member function (hence the name "erase-remove idiom").

  • How to use remove-erase idiom for removing empty vectors in a vector?
  • Erasing elements from a vector


回答3:

std::remove and std::remove_if do not actually remove anything but just give you an iterator by which you can then erase elements using the appropriate member function of whatever container you use. In std::vector's case, erase.

I invite you to read this old article from Scott Meyers: "My Most Important C++ Aha! Moments...Ever":

It was thus with considerable shock and a feeling of betrayal that I discovered that applying remove to a container never changes the number of elements in the container, not even if you ask it to remove everything. Fraud! Deceit! False advertising!



回答4:

See http://en.wikipedia.org/wiki/Erase-remove_idiom

std::remove_if doesn't actually obliterate erase the elements. What it does is move the elements that satisfies the criteria into the end of the range. It then returns an iterator to the first element of the removed (which are actually just moved) elements. It is on you then to erase that range from the container.

vector<foo> vec;
auto remove_start = remove_if(vec.begin(), vec.end(), [](foo & f) { return f.release() == true; });

vec.erase(remove_start, vec.end());

or

vec.erase(remove_if(vec.begin(), vec.end(),
                    [](foo & f) { return f.release() == true; }),
          vec.end());