J'aime la réponse de Matthieu, mais je vais reformuler l'organigramme comme suit:
Quand NE PAS utiliser std :: vector
Par défaut, si vous avez besoin d'un conteneur de trucs, utilisez std::vector. Ainsi, tout autre conteneur n'est justifié qu'en fournissant une fonctionnalité alternative à std::vector.
Constructeurs
std::vectorexige que son contenu soit constructible par déplacement, car il doit être capable de mélanger les éléments. Ce n'est pas un fardeau terrible à placer sur le contenu (notez que les constructeurs par défaut ne sont pas nécessaires , grâce à emplaceet ainsi de suite). Cependant, la plupart des autres conteneurs ne nécessitent aucun constructeur particulier (encore une fois, grâce à emplace). Donc, si vous avez un objet pour lequel vous ne pouvez absolument pas implémenter un constructeur de déplacement, vous devrez choisir autre chose.
Un std::dequeserait le remplacement général, ayant de nombreuses propriétés de std::vector, mais vous ne pouvez insérer qu'aux deux extrémités du deque. Les inserts au milieu nécessitent un déplacement. A std::listn'impose aucune exigence sur son contenu.
Besoin de booléens
std::vector<bool>n'est pas. Eh bien, c'est standard. Mais ce n'est pas un vectordans le sens habituel, car les opérations qui le std::vectorpermettent normalement sont interdites. Et il ne contientbool certainement pas l' art .
Par conséquent, si vous avez besoin d'un vectorcomportement réel d'un conteneur de bools, vous n'allez pas l'obtenir std::vector<bool>. Vous devrez donc respecter un std::deque<bool>.
Recherche
Si vous avez besoin de trouver des éléments dans un conteneur et que la balise de recherche ne peut pas être simplement un index, vous devrez peut-être abandonner std::vectoren faveur de setet map. Notez le mot clé « peut »; un tri std::vectorest parfois une alternative raisonnable. Ou Boost.Container's flat_set/map, qui implémente un fichier trié std::vector.
Il existe désormais quatre variantes de celles-ci, chacune ayant ses propres besoins.
- Utilisez a
maplorsque la balise de recherche n'est pas la même chose que l'élément que vous recherchez. Sinon, utilisez un set.
- À utiliser
unorderedlorsque vous avez beaucoup d'éléments dans le conteneur et que les performances de recherche doivent absolument l'être O(1)plutôt que O(logn).
- À utiliser
multisi vous avez besoin que plusieurs éléments aient la même balise de recherche.
Commande
Si vous avez besoin qu'un conteneur d'éléments soit toujours trié en fonction d'une opération de comparaison particulière, vous pouvez utiliser un fichier set. Ou multi_setsi vous avez besoin de plusieurs éléments pour avoir la même valeur.
Ou vous pouvez utiliser un trié std::vector, mais vous devrez le garder trié.
Stabilité
L'invalidation des itérateurs et des références pose parfois problème. Si vous avez besoin d'une liste d'éléments, de telle sorte que vous ayez des itérateurs / pointeurs vers ces éléments à divers autres endroits, alors std::vectorl'approche de l'invalidation peut ne pas être appropriée. Toute opération d'insertion peut entraîner une invalidation, selon la taille et la capacité actuelles.
std::listoffre une garantie ferme: un itérateur et ses références / pointeurs associés ne sont invalidés que lorsque l'élément lui-même est retiré du conteneur. std::forward_listest là si la mémoire est un problème sérieux.
Si c'est une garantie trop forte, std::dequeoffre une garantie plus faible mais utile. L'invalidation résulte d'insertions au milieu, mais les insertions en tête ou en queue ne provoquent que l'invalidation des itérateurs , pas des pointeurs / références aux éléments dans le conteneur.
Performances d'insertion
std::vector ne fournit qu'une insertion bon marché à la fin (et même dans ce cas, cela devient coûteux si vous soufflez de capacité).
std::listest cher en termes de performances (chaque élément nouvellement inséré coûte une allocation de mémoire), mais il est cohérent . Il offre également la capacité parfois indispensable de mélanger des articles pour pratiquement aucun coût de performance, ainsi que d'échanger des articles avec d'autres std::listconteneurs du même type sans perte de performance. Si vous devez beaucoup mélanger les choses , utilisez std::list.
std::dequefournit une insertion / retrait en temps constant à la tête et à la queue, mais l'insertion au milieu peut être assez coûteuse. Donc, si vous avez besoin d'ajouter / supprimer des éléments de l'avant comme de l'arrière, c'est std::dequepeut-être ce dont vous avez besoin.
Il convient de noter que, grâce à la sémantique de déplacement, std::vectorles performances d'insertion peuvent ne pas être aussi mauvaises qu'auparavant. Certaines implémentations ont implémenté une forme de copie d'élément basée sur la sémantique (la soi-disant "swaptimisation"), mais maintenant que le déplacement fait partie du langage, il est mandaté par le standard.
Aucune allocation dynamique
std::arrayest un bon conteneur si vous voulez le moins d'allocations dynamiques possibles. C'est juste un wrapper autour d'un C-array; cela signifie que sa taille doit être connue au moment de la compilation . Si vous pouvez vivre avec cela, utilisez std::array.
Cela étant dit, utiliser std::vectoret utiliser reserveune taille fonctionnerait tout aussi bien pour un borné std::vector. De cette façon, la taille réelle peut varier et vous n'obtenez qu'une seule allocation de mémoire (sauf si vous faites exploser la capacité).