20

假设我有std::vector<std::pair<int,Direction>>.

我正在尝试使用 erase-remove_if 习惯用法从向量中删除对。

stopPoints.erase(std::remove_if(stopPoints.begin(),
                                stopPoints.end(),
                                [&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; }));

我想删除所有将 .first 值设置为 4 的对。

在我的示例中,我有对:

- 4, Up
- 4, Down
- 2, Up
- 6, Up

但是,在我执行erase-remove_if 之后,我只剩下:

- 2, Up
- 6, Up
- 6, Up

我在这里做错了什么?

4

2 回答 2

39

正确的代码是:

stopPoints.erase(std::remove_if(stopPoints.begin(),
                                stopPoints.end(),
                                [&](const stopPointPair stopPoint)-> bool 
                                       { return stopPoint.first == 4; }), 
                 stopPoints.end());

您需要删除从返回的迭代器开始std::remove_if到向量末尾的范围,而不仅仅是单个元素。

“为什么?”

  • std::remove_if交换向量内的元素,以便将所有与谓词不匹配的元素放在容器的开头。这意味着如果谓词(lambda 函数的主体)返回true,则该元素将放置在向量的末尾

  • remove_if然后**返回一个迭代器,它指向与谓词**匹配的第一个元素。换句话说,指向要删除的第一个元素的迭代器。

  • std::vector::erase 擦除从返回的迭代器到向量末尾的范围,以便删除与谓词匹配的所有元素


更多信息: Erase-remove idiom (Wikipedia)

于 2016-08-18T13:44:40.250 回答
13

The method std::vector::erase has two overloads:

iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );

The first one only remove the element at pos while the second one remove the range [first, last).

Since you forget the last iterator in your call, the first version is chosen by overload resolution, and you only remove the first pair shifted to the end by std::remove_if. You need to do this:

stopPoints.erase(std::remove_if(stopPoints.begin(),
                                stopPoints.end(),
                                [&](const stopPointPair stopPoint)-> bool { 
                                    return stopPoint.first == 4; 
                                }), 
                 stopPoints.end());

The erase-remove idiom works as follow. Let say you have a vector {2, 4, 3, 6, 4} and you want to remove the 4:

std::vector<int> vec{2, 4, 3, 6, 4};
auto it = std::remove(vec.begin(), vec.end(), 4);

Will transform the vector into {2, 3, 6, A, B} by putting the "removed" values at the end (the values A and B at the end are unspecified (as if the value were moved), which is why you got 6 in your example) and return an iterator to A (the first of the "removed" value).

If you do:

vec.erase(it)

...the first overload of std::vector::erase is chosen and you only remove the value at it, which is the A and get {2, 3, 6, B}.

By adding the second argument:

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

...the second overload is chosen, and you erase value between it and vec.end(), so both A and B are erased.

于 2016-08-18T13:47:08.400 回答