web-dev-qa-db-fra.com

std :: remove_if - lambda, ne supprimant rien de la collection

Ok, je suppose que j'ai fait une erreur stupide ici. J'ai une liste de DisplayDevice3d et chaque DisplayDevice3d contient une liste de DisplayMode3d. Je veux supprimer tous les éléments de la liste des DisplayDevice3d qui n'ont pas de DisplayMode3d. J'essaie d'utiliser une Lambda pour le faire, c'est-à-dire:

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

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

Même si sur 6 DisplayMode3d dans MyDisplayDevices, seulement 1 a des DisplayMode3d dans sa collection Modes, rien n'est supprimé de la liste.

Quelle erreur vide j'ai fait ici?

Éditer:

Ah ok, mon erreur était que je devrais utiliser MyDisplayDevices.remove_if au lieu de std :: remove_if, cependant les réponses ci-dessous sont correctes pour l'utilisation de std :: remove_if: p.

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

Vous devez appeler erase sur l'itérateur renvoyé par remove_if, il devrait ressembler à ceci:

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 ne supprime rien de la liste, il les déplace simplement vers la fin. Vous devez l'utiliser avec erase. Voir ceci question pour plus de détails.

17
Asha

remove_if N'effectue pas de redimensionnement, mais renvoie simplement l'itérateur à l'élément qui suit le dernier élément non supprimé. Cet itérateur peut être passé à erase() pour effectuer le nettoyage.

enter image description here

8
Saurav Sahu

Comme d'autres l'ont mentionné, il existe des moyens de le faire fonctionner. Cependant, mon conseil serait d'éviter complètement remove_if Et de s'en tenir à une suppression basée sur un itérateur standard à la place. L'idiome ci-dessous fonctionne à la fois pour list et vector et ne produit pas de comportement inattendu.

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

Comme le mentionnent les commentaires ci-dessous, cette méthode a un coût plus élevé que remove_if Lorsque plus d'un élément est supprimé.

remove_if Fonctionne en copiant les éléments de plus loin dans le vecteur et en écrasant les vecteurs qui devraient être supprimés du vecteur par celui qui se trouve juste devant lui. Par exemple: remove_if appelé sur un vecteur pour supprimer tous les 0 éléments:

0 1 1 0 1 0

résulte en:

1 1 1 0 1 0

Notez que le vecteur n'est pas encore correct. C'est parce que remove_if Renvoie un itérateur au dernier élément valide ... il ne redimensionne pas automatiquement le vecteur. Vous devez toujours appeler v.erase() sur l'itérateur renvoyé de votre appel à remove_if.

Un exemple est ci-dessous

#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