简体   繁体   English

使用remove_if从向量中删除元素

[英]Removing elements from vector using remove_if

I am trying to remove vector elements using remove_if . 我试图使用remove_if删除矢量元素。 But unsuccessfully. 但没有成功。 What am I doing wrong? 我究竟做错了什么?

Here's my code: 这是我的代码:

#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);
}

My output is just: 我的输出只是:

1 2 3 4 5 6 1 2 3 4 5 6

Expected output: 预期产量:

1 2 3 4 5 6 1 3 4 5 6 1 2 3 4 5 6 1 3 4 5 6

You are using an overload of the std::vector::erase() member function that takes a single iterator as parameter. 您正在使用std::vector::erase()成员函数的重载,该函数将单个迭代器作为参数。 As the argument to erase() you are providing the iterator a.end() , since the following expression: 作为erase()的参数,你提供了迭代器a.end() ,因为下面的表达式:

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

evaluates to a.end() (ie, because of the comma operator). 计算到a.end() (即,因为逗号运算符)。

The iterator passed to the overload of erase() that takes a single iterator must be dereferenceable . 传递给erase()的重载的迭代器必须是可解除引用的单个迭代器。 However, the iterator a.end() is not dereferenceable, therefore, the call to erase() results in undefined behavior . 但是,迭代器a.end()不能解除引用,因此,对erase()的调用会导致未定义的行为


To use the overload that takes two iterators, remove the parenthesis surrounding the call to std::remove_if : 要使用带有两个迭代器的重载,请删除对std::remove_if的调用周围的括号:

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

You're adding superfluous parentheses, change it to 您正在添加多余的括号,将其更改为

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

Note that the comma operator just returns the last operand, that means you're passing a.end() to erase , which leads to UB. 请注意, 逗号运算符只返回最后一个操作数,这意味着您将a.end()传递给erase ,这将导致UB。

The other answers have pointed out what the problem was. 其他答案指出了问题所在。 I want to say, it will be easier to notice these kinds of problems by simplifying your code. 我想说,通过简化代码可以更容易地注意到这些问题。

I suggest using: 我建议使用:

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);
}

You just had too many parentheses in the function call. 你在函数调用中只有太多的括号。

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

Just remove one parentheses before the std::remove_if and at the end of the call. 只需在std::remove_if之前和调用结束时删除一个括号。

Your problem is that you are doing the erase-remove idiom inline. 你的问题是你正在内联擦除删除习语。 It is very error prone. 这很容易出错。

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) );
}

this little helper function does the error prone part of erase remove in isolation from other noise. 这个小帮手功能可以将易出错的部分擦除与其他噪声隔离开来。

Then: 然后:

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

becomes

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

and suddenly your code works. 突然你的代码工作了。

Now the proximate cause was you had a typo: 现在最直接的原因是你有一个错字:

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

here I have expanded the line's structure. 在这里我扩展了线的结构。 You can see from the above that you only passed one argument to erase ; 你可以从上面看到你只传递了一个参数来erase ; namely a.end() , because you passed it ( some remove expression, a.end() ) in brackets. a.end() ,因为你在括号中传递了它( some remove expression, a.end() ) This invoked the comma operator: so it ran the remove expression (moving the element 2 to the end), then discarded the returned iterator and evaluated to a.end() . 这调用了逗号运算符:所以它运行了remove表达式(将元素2移动到结尾),然后丢弃返回的迭代器并计算到a.end()

We then passed a.end() to erase , which is not a valid iterator to pass to erase . 然后我们将a.end()传递给erase ,这不是传递给erase的有效迭代器。 So your program is ill-formed, and UB results. 所以你的程序是不正确的,UB结果。

That is only the proximate cause. 这只是近因 There are many mistakes you can easily make when manually doing erase-remove; 手动擦除时,您可以轻松犯下许多错误; the code is fragile and full of repetition. 代码很脆弱,充满了重复。

DRY is the principle that you want a single point of customization, and you don't want to repeat things that don't need repeating. DRY是您希望单点自定义的原则,并且您不想重复不需要重复的事情。 erase_remove_if is my attempt to apply DRY to avoid exactly this kind of error. erase_remove_if是我尝试应用DRY以避免这种错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM