Définit * les * deux éléments et la capacité initiale de std :: vector

J’ai lu dans cet article que la capacité initiale d’un std::vector ne pouvait pas être contrôlée par son constructeur. La meilleure façon de le définir, si vous savez que sa taille sera constante au moment de l’exécution, semble alors être:

 const int size; std::vector v; v.reserve(size); 

Mais, comme T est une grande classe, je suis heureux d’utiliser l’argument d’initialisation du constructeur std::vector v(size, T()); . Quel est alors le meilleur moyen d’allouer uniquement la mémoire dont j’ai besoin sans avoir à parcourir manuellement les éléments pour les initialiser?

 std::vector v; v.reserve(size); for(T t : v) t = T(); // `has to call T::operator= (that may not even be defined) 

ou

 std::vector v(size, T()); // may allocate more memory than I need.. v.shrink_to_fit(); // ..then deallocate it immediately (pointless) 

Quel serait le plus proche de mon idéal:

 std::vector v(size, T(), /*capacity =*/ size); 

[EDIT]: Il ressort de vos réponses que ce dont j’ai besoin plus précisément est que v soit rempli avec des instances de size de T , chaque construction utilisant le constructeur par défaut de T et non copiée. Comment puis-je faire car je ne peux pas utiliser une liste d’initialiseur lorsque la size n’est pas connue au moment de la compilation?


Bonus: Au fait, pourquoi n’y a-t-il pas moyen de choisir la capacité initiale d’un vecteur?

On ne sait pas comment vous voulez que tous les éléments soient construits. Supposons que vous souhaitiez que le vecteur ait n éléments et que vous desired_capacity capacité desired_capacity , qui peut être égale à n .

Si vous voulez n éléments identiques, essayez ceci:

 std::vector v(n, t); // creates n copies of the value tvreserve(desired_capacity); 

Si vous ne souhaitez pas spécifier de valeur initiale, vous pouvez l’omettre:

 std::vector v(n); // creates n copies of T, default constructed. v.reserve(desired_capacity); 

Si vous souhaitez spécifier individuellement un certain nombre d’éléments, essayez une liste d’initialiseurs.

 std::vector({t1, t2, ..., tn}); // initializer list v.reserve(desired_capacity); 

La liste des possibilités continue; pour plus d’informations, consultez la référence vector::vector . L’utilisation de la construction “sur place” signifie que vous pouvez renseigner un vecteur avec des types non constructibles en copie.

Une autre option, pour remplir un vecteur avec des types non copiables, consiste à utiliser emplace_back à plusieurs resockets:

 std::vector v; v.reserve(n); for (int i = 0; i < n; ++i) { v.emplace_back(x, y, z); // Construct T(x, y, z) in-place } 

Une remarque à propos de votre question initiale: votre boucle for contient une erreur:

 for(T t : v) t = T(); // almost certainly want for (T& t : v) ... // in order to get references to modify vector elements. 

Bonus en premier:

Bonus: Au fait, pourquoi n’y a-t-il pas moyen de choisir la capacité initiale d’un vecteur?

Parce que les concepteurs de la classe n’ont jamais jugé assez important d’inclure.


Je ne connais aucune implémentation où après:

 std::vector v(size, T()); 

l’affirmation suivante ne tient pas:

 assert(v.capacity() == size); 

Ce n’est pas garanti par la norme à tenir. Mais cela semble être la norme de facto.

On ne peut cependant pas transférer ce peu de savoir à std::ssortingng .


Si vous souhaitez exiger positivement que capacity() == size() après la construction, vous pouvez utiliser shrink_to_fit() . Ceci étant dit, shrink_to_fit() n’est pas garanti par le standard, et s’il fait quoi que ce soit, il doublera temporairement vos besoins en mémoire. Une telle utilisation pourrait ressembler à:

 std::vector v(size, T()); if (v.capacity() > v.size()) v.shrink_to_fit(); // If executed and successful, this will allocate a new buffer if (v.capacity() > v.size()) // now what? 

L’utilisation de reserve() n’est plus efficace. Après reserve() , capacity() est supérieur ou égal à l’argument de reserve si la réaffectation a lieu; et égal à la valeur précédente de capacity() sinon.

Les constructeurs de vecteurs qui spécifient la taille initiale doivent tous effectuer une itération pour initialiser les éléments. Même si vous n’avez pas de code qui itère explicitement pour initialiser des éléments, c’est ce qui se passe à l’intérieur du constructeur de vecteur. Donc, je ne vois pas le problème à faire:

 template std::vector create_vector_with_capacity(size_t size, size_t capacity, Args... args) { std::vector v; v.reserve(capacity); for (size_t i = 0; i != size; ++i) v.emplace_back( args... ); return v; } 

Exemple d’utilisation:

 std::vector vec = create_vector_with_capacity(5, 20, "foo"); 

La sémantique de déplacement de std::vector assure que, même si copy-elision est désactivé et que T n’est pas déplaçable, le vecteur n’est pas copié, même s’il est retourné par valeur.

Cette version construit chaque élément sur place à l’aide d’ args (ou du constructeur par défaut si vous n’avez pas fourni d’arguments). Vous pouvez également remplacer Args... args et emplace_back par T&& t = T() et push_back , ce qui permet de copier-construire tous les éléments d’un élément (éventuellement temporaire) que vous transmettez.

Remarque: En réalité, la norme ne garantit pas de manière absolue que le déplacement d’un vecteur conserve sa capacité (pas à ma connaissance, de toute façon); si votre implémentation ne le fait pas, ou si vous voulez vraiment garantir la rétention de capacité, alors, au lieu de renvoyer v , faites-en un paramètre passé par référence pour lequel l’appelant doit fournir un vecteur vide.

Vous pouvez faire:

 std::vector v; v.reserve(size); // don't construct any T. v.size() == 0. v.resize(size); // do construct T. 

vous pouvez construire un vecteur avec une capacité et une valeur initiale,

 std::vector vs(5, "abc"); std::vector> x(6, std::pair(1, 2));