web-dev-qa-db-ja.com

std :: remove_if-ラムダ、コレクションから何も削除しない

わかりました、私はここで愚かな間違いを犯したと思います。 DisplayDevice3dのリストがあり、各DisplayDevice3dにはDisplayMode3dのリストが含まれています。 DisplayMode3dを持たないDisplayDevice3dのリストからすべてのアイテムを削除したい。私はそれを行うためにラムダを使用しようとしています、すなわち:

    // 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のうち、ModesコレクションにDisplayMode3dがあるのは1つだけですが、リストからは何も削除されていません。

私はここでどのようなミスを犯しましたか?

編集:

ああ、私の間違いは、std :: remove_ifの代わりにMyDisplayDevices.remove_ifを使用しなければならないことでしたが、以下の回答はstd :: remove_if:pの使用に適しています。

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

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());
71
Matthieu N.

remove_ifはリストから何も削除せず、単に最後に移動します。 eraseとともに使用する必要があります。詳細は question をご覧ください。

17
Asha

_remove_if_はサイズ変更を実行しませんが、代わりに、削除されていない最後の要素に続く要素に反復子を返すだけです。このイテレータは、erase()に渡してクリーンアップを実行できます。

enter image description here

8
Saurav Sahu

他の人が言及したように、それを機能させる方法があります。しかし、私のアドバイスは_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
_

以下のコメントが言及しているように、このメソッドは複数の要素が削除されたとき_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)
}
_
2
bobobobo