有没有比 std::remove_if 更好的替代方法来从向量中删除元素?

2022-01-07 00:00:00 c++ c++11 stl c++17 erase-remove-idiom

std::vector 或其他容器中删除具有特定属性的元素的任务有助于实现函数式风格:为什么要为循环、内存释放和正确移动数据而烦恼?

The task of removing elements with a certain property from a std::vector or other container lends itself to a functional style implementation: Why bother with loops, memory deallocation and moving data around correctly?

但是,在 C++ 中执行此操作的标准方法似乎是以下习语:

However the standard way of doing this in C++ seems to be the following idiom:

std::vector<int> ints;
...
ints.erase(
    std::remove_if(ints.begin(), 
                   ints.end(),
                   [](int x){return x < 0;}),
    ints.end());

此示例从整数向量中删除所有小于零的元素.

This example removes all elements less than zero from an integer vector.

我发现它不仅丑,而且容易误用.很明显 std::remove_if 不能改变向量的大小(正如它的名字所暗示的那样),因为它只能传递迭代器.但是很多开发者,包括我自己,一开始并没有意识到这一点.

I find it not only ugly but also easy to use incorrectly. It is clear that std::remove_if cannot change the size of the vector (as its name would suggest) because it only gets iterators passed. But many developers, including myself, don't get that in the beginning.

那么有没有更安全、更优雅的方式来实现这一目标?如果没有,为什么?

So is there a safer and hopefully more elegant way to achieve this? If not, why?

推荐答案

我发现它不仅丑,而且容易误用.

I find it not only ugly but also easy to use incorrectly.

别担心,我们一开始都做到了.

Don't worry, we all did at the start.

很明显, std::remove_if 不能改变向量的大小(正如它的名字所暗示的那样),因为它只会让迭代器通过.但是很多开发者,包括我自己,一开始并没有意识到这一点.

It is clear that std::remove_if cannot change the size of the vector (as its name would suggest) because it only gets iterators passed. But many developers, including myself, don't get that in the beginning.

一样.它让每个人都感到困惑.多年前,它可能不应该被称为 remove_if.事后诸葛亮,嗯?

Same. It confuses everyone. It probably shouldn't have been called remove_if all those years ago. Hindsight, eh?

那么有没有更安全、更优雅的方式来实现这一目标?

So is there a safer and hopefully more elegant way to achieve this?

没有

如果不是,为什么?

因为这是从容器中删除项目时保持性能的最安全、最优雅的方式,其中删除项目会使迭代器失效.

Because this is the safest, most elegant way that preserves performance when deleting items from a container in which deleting an item invalidates iterators.

期待:

有什么我可以做的吗?

是的,把这个习语包装成一个函数

Yes, wrap this idiom into a function

template<class Container, class F>
auto erase_where(Container& c, F&& f)
{
    return c.erase(std::remove_if(c.begin(), 
                                  c.end(),
                                  std::forward<F>(f)),
                   c.end());    
}

激励示例中的调用然后变为:

The call in the motivating example then becomes:

auto is_negative = [](int x){return x < 0;};
erase_where(ints, is_negative);

erase_where(ints, [](int x){return x < 0;});

相关文章