使用remove_if从向量中删除元素

时间:2018-07-31 05:24:01

标签: c++ c++11 vector stl

我正在尝试使用remove_if删除矢量元素。但是没有成功。我究竟做错了什么?

这是我的代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

void printme(std::vector<int>& a){
    for(const auto& item: a)
    std::cout << item << std::endl;
}

int main()
{
    std::vector<int> a {1, 2, 3, 4, 5, 6};
    printme(a);  
    a.erase( (std::remove_if(a.begin(), a.end(), [](const int& x){
        return x == 2;
        }), a.end()));
    printme(a);
}

我的输出是:

  

1 2 3 4 5 6

预期输出:

  

1 2 3 4 5 6 1 3 4 5 6

5 个答案:

答案 0 :(得分:19)

您正在使用std::vector::erase()成员函数的重载,该重载将单个迭代器作为参数。作为erase()的参数,您要提供迭代器a.end(),因为以下表达式:

(std::remove_if(a.begin(), a.end(), [](const int& x){ return x == 2; }), a.end()))

评估为a.end()(即,由于逗号运算符)。

传递给具有单个迭代器的erase()重载的迭代器必须是 dereferenceable 。但是,迭代器a.end()不可取消引用,因此,对erase()的调用会导致未定义的行为


要使用需要两个迭代器的重载,请删除对std::remove_if的调用周围的括号:

a.erase(std::remove_if(a.begin(), a.end(), [](const int& x){
        return x == 2;
        }), a.end());

答案 1 :(得分:8)

您要添加多余的括号,将其更改为

a.erase( std::remove_if(a.begin(), a.end(), [](const int& x){
    return x == 2;
    }), a.end());

请注意,comma operator仅返回最后一个操作数,这意味着您正在将a.end()传递给erase,这将导致UB。

答案 2 :(得分:7)

其他答案指出了问题所在。我想说的是,通过简化代码,可以更容易注意到这些类型的问题。

我建议使用:

int main()
{
   std::vector<int> a {1, 2, 3, 4, 5, 6};
   printme(a);  

   auto it = std::remove_if(a.begin(), a.end(), [](const int& x){ return x == 2; });
   a.erase(it, a.end());

   printme(a);
}

答案 3 :(得分:5)

函数调用中括号过多。

a.erase(std::remove_if(a.begin(), a.end(), [](const int& x) {return x == 2;}), a.end());

只需在std::remove_if之前和通话结束时删除一个括号即可。

答案 4 :(得分:3)

您的问题是您正在内联执行“擦除-删除”习惯用法。这很容易出错。

char[]

这个小辅助功能可以将擦除的容易出错的部分与其他噪音隔离开来。

然后:

template<class C, class F>
void erase_remove_if( C&& c, F&& f ) {
  using std::begin; using std::end;
  auto it = std::remove_if( begin(c), end(c), std::forward<F>(f) );
  c.erase( it, end(c) );
}

成为

a.erase( (std::remove_if(a.begin(), a.end(), [](const int& x){
    return x == 2;
    }), a.end()));

您的代码突然起作用。

现在最直接的原因是您有错字:

erase_remove_if(
  a,
  [](const int& x){
    return x == 2;
  }
);

在这里,我扩展了生产线的结构。从上面可以看到,您仅将一个参数传递给a.erase( ( std::remove_if( a.begin(), a.end(), [](const int& x){ return x == 2; } ), a.end() ) ); ;即erase,因为您在括号中传递了a.end()。这会调用逗号运算符:因此它运行remove表达式(将元素( some remove expression, a.end() )移动到末尾),然后丢弃返回的迭代器并求值为2

然后,我们将a.end()传递给a.end(),这不是传递给erase的有效迭代器。因此,您的程序格式不正确,并且会产生UB结果。

这只是最接近的原因。手动执行删除删除操作时,很容易犯很多错误。该代码易碎,并且重复很多。

DRY是原则,您需要单点自定义,并且您不想重复不需要重复的事情。 erase是我尝试应用DRY以避免这种错误的方法。