162 votes

Comment supprimer un élément d'un vecteur stl avec une certaine valeur ?

Je regardais la documentation de l'API pour stl vector, et j'ai remarqué qu'il n'y avait pas de méthode sur la classe vectorielle qui permettait de supprimer un élément avec une certaine valeur. Cela semble être une opération courante, et il semble étrange qu'il n'y ait pas de moyen intégré pour le faire.

2 votes

Je sais que je l'ai déjà mentionné plusieurs fois mais le livre de Scott Meyer STL efficace couvre ces écueils de manière claire.

1 votes

0 votes

Cette lecture pourrait être intéressante pour vous : fr.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

191voto

Jim Buck Points 10839

std::remove n'efface pas réellement l'élément du conteneur, mais il renvoie le nouvel itérateur de fin qui peut être passé à la commande container_type::erase pour effectuer la suppression réelle des éléments supplémentaires qui se trouvent maintenant à la fin du conteneur :

std::vector<int> vec;
// .. put in some values ..
int int_to_remove = n;
vec.erase(std::remove(vec.begin(), vec.end(), int_to_remove), vec.end());

3 votes

Est-ce que cette forme tout-en-un dépend de l'ordre dans lequel le compilateur évalue les arguments, ou est-ce que vec.end() garantie d'être la même de part et d'autre de l'appel pour std::remove ? Il me semble, en lisant d'autres parties du web, que c'est sans danger, mais cela devrait être clairement indiqué.

1 votes

C'est bon. Le résultat de vec.end() n'a pas besoin d'être la même ; elle doit simplement être correcte (ce qui est le cas).

8 votes

vec.end() doit être le même, mais ce n'est pas grave car std::remove ne le change pas. Si elle le changeait (et invalidait l'ancienne valeur), il y aurait un problème : l'ordre d'évaluation des paramètres n'est pas spécifié et vous ne sauriez donc pas si la seconde valeur vec.end() est toujours valable au moment où il est utilisé. La raison pour laquelle il en va de même est simple, std::remove ne change pas la taille du conteneur, il déplace simplement son contenu.

81voto

Etherealone Points 1289

Si vous voulez supprimer un le suivant sera un peu plus efficace.

std::vector<int> v;

auto it = std::find(v.begin(), v.end(), 5);
if(it != v.end())
    v.erase(it);

ou vous pouvez éviter les frais généraux de déplacement des articles si l'ordre n'a pas d'importance pour vous :

std::vector<int> v;

auto it = std::find(v.begin(), v.end(), 5);

if (it != v.end()) {
  using std::swap;

  // swap the one to be removed with the last element
  // and remove the item at the end of the container
  // to prevent moving all items after '5' by one
  swap(*it, v.back());
  v.pop_back();
}

4 votes

Notez que cela ne supprimera pas les doublons de l'élément s'ils existent, alors que l'approche std::remove_if le fait.

16voto

bradtgmurray Points 3999

Utilisez la méthode globale std::remove avec l'itérateur begin et end, puis utilisez std::vector.erase pour supprimer réellement les éléments.

Liens vers la documentation
std::remove http://www.cppreference.com/cppalgorithm/remove.html
std::vector.erase http://www.cppreference.com/cppvector/erase.html

std::vector<int> v;
v.push_back(1);
v.push_back(2);

//Vector should contain the elements 1, 2

//Find new end iterator
std::vector<int>::iterator newEnd = std::remove(v.begin(), v.end(), 1);

//Erase the "removed" elements.
v.erase(newEnd, v.end());

//Vector should now only contain 2

Merci à Jim Buck d'avoir signalé mon erreur.

0 votes

Elle a la capacité d'empêcher le déplacement d'autres éléments lors de l'effacement d'un élément au milieu du vecteur ; elle est donc plus rapide. Il les déplace d'abord à la fin du vecteur, puis vous pouvez simplement les effacer de la fin du vecteur.

5voto

nsanders Points 5282

Si vous avez un vecteur non trié, alors vous pouvez simplement échanger avec le dernier élément du vecteur puis resize() .

Avec un conteneur commandé, vous serez mieux avec std::vector::erase() . Notez qu'il existe un std::remove() défini dans <algorithm> mais cela ne fait pas réellement l'effacement. (Lisez attentivement la documentation).

5voto

Luke Halliwell Points 4402

Les autres réponses couvrent bien la façon de le faire, mais j'ai pensé que je devrais également souligner qu'il n'est pas vraiment étrange que cela ne soit pas dans l'API vectorielle : c'est une recherche linéaire inefficace dans le vecteur pour la valeur, suivie d'un tas de copies pour la supprimer.

Si vous effectuez cette opération de manière intensive, il peut être intéressant de considérer std::set à la place pour cette raison.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X