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

发布于 2024-10-08 17:35:28 字数 867 浏览 6 评论 0原文

好吧,我想我在这里犯了一个愚蠢的错误。我有一个 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; 
   }
  ); 

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

我在这里犯了什么愚蠢的错误?

编辑:

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

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

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.:

    // 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.

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( [](DisplayDevice3d const & device) 
                            { 
                                return device.Modes.size() == 0; 
                            });

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

我很OK 2024-10-15 17:35:28

您需要对从remove_if返回的迭代器调用erase,它应该看起来像这样:

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

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

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

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

MyDisplayDevices.erase(new_end, MyDisplayDevices.end());
丑疤怪 2024-10-15 17:35:28

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

remove_if doesn't remove anything from list it just moves them to end. You need to use it along with erase. See this question for more details.

舟遥客 2024-10-15 17:35:28

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

输入图像描述这里

remove_if doesn'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.

enter image description here

尽揽少女心 2024-10-15 17:35:28

正如其他人提到的,有多种方法可以使其发挥作用。不过,我的建议是完全避免 remove_if 并坚持使用标准的基于迭代器的删除。下面的习惯用法适用于 listvector 并且不会产生意外的行为。

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

正如下面的评论提到的,当删除超过 1 个元素时,此方法的成本确实比 remove_if 更高。

remove_if 的工作原理是从向量中更靠前的位置复制元素,并用紧邻其前面的向量覆盖应从向量中删除的向量。例如:remove_if 调用向量来删除所有 0 个元素:

0 1 1 0 1 0

结果是:

1 1 1 0 1 0

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

下面是一个例子

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

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

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_if when more than 1 element is removed.

remove_if works 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:

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_if returns 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.

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)
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文