C++ std::remove_if - lambda,不从集合中删除任何内容

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4478636/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:29:59  来源:igfitidea点击:

std::remove_if - lambda, not removing anything from the collection

c++c++11lambdaremove-iferase-remove-idiom

提问by Robinson

Ok, I expect I've made a dumb mistake here. I have a list of DisplayDevice3d and each DisplayDevice3d contains a list of DisplayMode3d. I want to remove all items from the list of DisplayDevice3d that don't have any DisplayMode3d's. I'm trying to use a Lambda to do it, ie.:

好吧,我想我在这里犯了一个愚蠢的错误。我有一个 DisplayDevice3d 列表,每个 DisplayDevice3d 都包含一个 DisplayMode3d 列表。我想从 DisplayDevice3d 列表中删除所有没有任何 DisplayMode3d 的项目。我正在尝试使用 Lambda 来做到这一点,即:

    // If the device doesn't have any modes, remove it.

  std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(),
   [](DisplayDevice3d& device) 
   { 
    return device.Modes.size() == 0; 
   }
  ); 

Even though out of 6 DisplayMode3d's in MyDisplayDevices, only 1 has any DisplayMode3d's in its Modes collection, nothing is being removed from the list.

尽管在 MyDisplayDevices 中的 6 个 DisplayMode3d 中,只有 1 个在其 Modes 集合中有任何 DisplayMode3d,但没有从列表中删除任何内容。

What numpty mistake have I made here?

我在这里犯了什么大错?

Edit:

编辑:

Ah ok, my mistake was I should be using MyDisplayDevices.remove_if instead of std::remove_if, however the answers below are correct for use of std::remove_if :p.

好吧,我的错误是我应该使用 MyDisplayDevices.remove_if 而不是 std::remove_if,但是下面的答案对于使用 std::remove_if :p 是正确的。

MyDisplayDevices.remove_if( [](DisplayDevice3d const & device) 
                            { 
                                return device.Modes.size() == 0; 
                            });

回答by

You need to call erase on the iterator returned from remove_if, It should look something like this:

您需要对从 remove_if 返回的迭代器调用擦除,它应该如下所示:

auto new_end = std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(),
                              [](const DisplayDevice3d& device)
                              { return device.Modes.size() == 0; });

MyDisplayDevices.erase(new_end, MyDisplayDevices.end());

回答by Asha

remove_ifdoesn't remove anything from list it just moves them to end. You need to use it along with erase. See this questionfor more details.

remove_if不会从列表中删除任何内容,只是将它们移到末尾。您需要将它与erase. 有关更多详细信息,请参阅此问题

回答by Saurav Sahu

remove_ifdoesn't perform resizing, but instead it just returns the iterator to the element that follows the last element not removed. This iterator can be passed to erase()to do the clean up.

remove_if不执行调整大小,而是仅将迭代器返回到未删除的最后一个元素之后的元素。可以传递此迭代器来进行erase()清理。

enter image description here

在此处输入图片说明

回答by bobobobo

As others have mentioned, there are ways to make it work. However my advice would be to completely avoid remove_ifand stick to a standard iterator-based removal instead. The idiom below works both for listand vectorand does not produce unexpected behavior.

正如其他人所提到的,有一些方法可以让它发挥作用。然而,我的建议是完全避免remove_if并坚持标准的基于迭代器的删除。下面的习语适用于list并且vector不会产生意外行为。

for( vector<TYPE>::iterator iter = vec.begin() ; iter != vec.end() ; )
  if( iter->shouldRemove )
    iter = vec.erase( iter ) ; // advances iter
  else
    ++iter ; // don't remove

As the comments below mention, this method does have a higher cost than remove_ifwhen more than 1 element is removed.

正如下面的评论所提到的,这种方法的成本确实remove_if比删除 1 个以上元素时的成本更高。

remove_ifworks by copying elements from further ahead in the vector, and overwriting vectors that should be removed from the vector by the one immediately in front of it. For example: remove_if called on a vector to remove all 0 elements:

remove_if工作原理是从向量中更靠前的位置复制元素,并覆盖应该从向量中移除的向量。例如:在向量上调用 remove_if 以删除所有 0 元素:

0 1 1 0 1 0

results in:

结果是:

1 1 1 0 1 0

Notice how the vector isn't correct yet. That is because remove_ifreturns an iterator to the last valid element... it doesn't automatically resize the vector. You still need to call v.erase()on the iterator returned from your call to remove_if.

注意向量是如何不正确的。那是因为remove_if返回一个迭代器到最后一个有效元素......它不会自动调整向量的大小。您仍然需要调用v.erase()从调用返回的迭代器remove_if

An example is below

下面是一个例子

#include <stdio.h>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

void print( vector<int> &v )
{
  for( int i : v )
    printf( "%d ", i );
  puts("");
}

int main()
{
  vector<int> v = { 0, 1, 1, 0, 1, 0 };
  print( v ); // 0 1 1 0 1 0
  vector<int>::iterator it = remove_if( v.begin(), v.end(), [](int i){ return i == 0; } );
  print( v ); // 1 1 1 0 1 0
  v.erase( it, v.end() ); // actually cut out values not wanted in vector
  print( v ); // 1 1 1 (correct)
}