使用erase-remove_if成语

ome*_*sbk 11 c++ erase-remove-idiom

让我说我有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; }));
Run Code Online (Sandbox Code Playgroud)

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

在我的例子中,我有成对:

- 4, Up
- 4, Down
- 2, Up
- 6, Up
Run Code Online (Sandbox Code Playgroud)

但是,在执行erase-remove_if后,我留下:

- 2, Up
- 6, Up
- 6, Up
Run Code Online (Sandbox Code Playgroud)

我在这做错了什么?

Vit*_*meo 22

正确的代码是:

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

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

"为什么?"

  • std::remove_if在向量内部交换元素,以便将与谓词不匹配的所有元素放到容器的开头.

    • 然后它返回指向第一个谓词匹配元素的迭代器.

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


更多信息: 擦除删除成语(维基百科).

  • 虽然这个答案有助于解决这个问题,但我怀疑它不太准确。根据 https://cplusplus.com/reference/algorithm/remove_if/ 中的示例实现,并非所有已删除的元素都移动到向量的末尾,而是可能简单地被最初放置在它们前面的未删除元素覆盖。返回迭代器之后的位置可能包含已删除的元素,或现在位于向量开头的未删除元素的原始副本(如果这些元素具有移动语义,则那些移动到开头的元素将无效,因为它们已被移动) 。 (3认同)

Hol*_*olt 7

该方法std::vector::erase有两个重载:

iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );
Run Code Online (Sandbox Code Playgroud)

第一个只删除元素,pos而第二个删除范围[first, last).

由于您last在调用中忘记了迭代器,因此第一个版本是通过重载分辨率选择的,并且您只删除第一个移位到最后的对std::remove_if.你需要这样做:

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

擦除,删除成语工作原理如下:假设你有一个载体{2, 4, 3, 6, 4},你要删除的4:

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

{2, 3, 6, A, B}通过将"已移除"值放在最后来转换向量(值AB结尾都未指定(就像移动了值一样),这就是为什么你得到6了你的例子)并返回一个迭代器A(第一个)被"删除"的价值).

如果你这样做:

vec.erase(it)
Run Code Online (Sandbox Code Playgroud)

std::vector::erase选择第一个重载,你只删除at的值it,即Aget和get {2, 3, 6, B}.

通过添加第二个参数:

vec.erase(it, vec.end())
Run Code Online (Sandbox Code Playgroud)

第二过载被选择,并且你之间擦除值itvec.end(),这样既AB被擦除.


Ida*_*anB 7

我知道在提出这个问题时没有 C++20,所以只需添加这个问题的完整性答案和最新答案,C++20 现在使用std具有更清晰和更简洁的模式::erase_if

请参阅通用代码示例:

#include <vector>   
int main()
    {
        std::vector<char> cnt(10);
        std::iota(cnt.begin(), cnt.end(), '0');
     
        auto erased = std::erase_if(cnt, [](char x) { return (x - '0') % 2 == 0; });
        std::cout << erased << " even numbers were erased.\n";
    }
Run Code Online (Sandbox Code Playgroud)

具体问题代码片段:

std::erase_if(stopPoints, [&](const stopPointPair stopPoint)-> bool { 
    return stopPoint.first == 4; 
});
Run Code Online (Sandbox Code Playgroud)

请在此处查看完整的详细信息:
https ://en.cppreference.com/w/cpp/container/vector/erase2