std :: remove_if - lambda, ne pas enlever quoi que ce soit de la collection
-
11-10-2019 - |
Question
Ok, je pense que je l'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 DisplayMode3d de. Je suis en train d'utiliser un Lambda pour le faire, à savoir:.
// If the device doesn't have any modes, remove it.
std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(),
[](DisplayDevice3d& device)
{
return device.Modes.size() == 0;
}
);
Bien que de 6 DisplayMode3d est dans MyDisplayDevices, seulement 1 a tout de DisplayMode3d dans sa collection Modes, rien est retiré de la liste.
Quelle erreur ont numpty je fait ici?
Edit:
Ah ok, mon erreur était que je devrais utiliser MyDisplayDevices.remove_if au lieu de std :: remove_if, mais 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;
});
La solution
Vous devez appeler effacer le retour de iterator 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());
Autres conseils
remove_if
ne supprime rien de la liste, il suffit de les déplace à la fin. Vous devez l'utiliser avec erase
. Voir cette question pour plus de détails.
Comme d'autres l'ont mentionné, il existe des moyens pour le faire fonctionner. Cependant, mon conseil serait d'éviter complètement remove_if
et le bâton à une norme basée retrait iterator au lieu. L'idiome ci-dessous pour les deux œuvres list
et vector
et ne produit pas un 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 les commentaires ci-dessous mention, cette méthode a un coût plus élevé que lorsque l'élément remove_if
plus de 1 est supprimé.
remove_if
fonctionne en copiant des éléments de plus loin dans le vecteur, et l'écrasement des vecteurs qui doivent être retirés du vecteur par l'une immédiatement en avant de celui-ci. Par exemple: remove_if appelé un vecteur pour supprimer tous les éléments 0:
0 1 1 0 1 0
résultats dans:
1 1 1 0 1 0
Remarquez comment le vecteur est pas encore correct. C'est parce que remove_if
retourne un itérateur au dernier élément valide ... il ne redimensionne pas automatiquement le vecteur. Vous devez toujours appel v.erase()
sur le iterator retour 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)
}