【问题标题】:Using erase-remove_if idiom使用erase-remove_if 成语
【发布时间】:2016-12-25 11:30:18
【问题描述】:

假设我有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

我在这里做错了什么?

【问题讨论】:

标签: c++ erase-remove-idiom


【解决方案1】:

正确的代码是:

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,那么该元素将被放置在向量的 end 处。

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

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


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

【讨论】:

    【解决方案2】:

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

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

    第一个仅删除 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());
    

    erase-remove 成语的作用如下。假设您有一个向量{2, 4, 3, 6, 4},并且您想删除4

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

    将通过将“已移除”值放在末尾将向量转换为 {2, 3, 6, A, B}(末尾的值 AB 未指定(好像该值被移动了 ),这就是为什么您在示例中得到 6 的原因)并将迭代器返回到 A(“删除”值的第一个)。

    如果你这样做:

    vec.erase(it)
    

    ...选择std::vector::erase 的第一个重载,您只需删除it 处的值,即A 并获得{2, 3, 6, B}

    通过添加第二个参数:

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

    ...选择了第二个重载,你擦除了itvec.end() 之间的值,所以AB 都被擦除了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-07
      • 1970-01-01
      • 2011-02-10
      • 2018-10-29
      • 2016-02-26
      • 1970-01-01
      • 1970-01-01
      • 2013-05-07
      相关资源
      最近更新 更多