vector<int> v;
v.push_back(1);
v.push_back(v[0]);
Si le second push_back provoque une réallocation, la référence au premier entier du vecteur ne sera plus valide. Donc ce n'est pas sûr?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
Cela le rend sûr?
push_back
. Une autre affiche a noté un bogue , qu'il ne traitait pas correctement le cas que vous décrivez. Personne d'autre, pour autant que je sache, n'a soutenu que ce n'était pas un bug. Ne pas dire que c'est une preuve concluante, juste une observation.Réponses:
Il semble que http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 a abordé ce problème (ou quelque chose de très similaire) comme un défaut potentiel de la norme:
La résolution proposée était qu'il ne s'agissait pas d'un défaut:
la source
v.insert(v.begin(), v[2]);
ne peut pas déclencher une réallocation. Alors, comment cela répond-il à la question?Oui, c'est sûr, et les implémentations de bibliothèques standard sautent à travers les obstacles pour y parvenir.
Je pense que les développeurs font remonter cette exigence à 23.2 / 11 d'une manière ou d'une autre, mais je ne peux pas comprendre comment, et je ne peux pas trouver quelque chose de plus concret non plus. Le mieux que je puisse trouver est cet article:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
L'inspection des implémentations de libc ++ et libstdc ++ montre qu'elles sont également sûres.
la source
vec.insert(vec.end(), vec.begin(), vec.end());
?vector.push_back
spécifie le contraire. "Provoque une réallocation si la nouvelle taille est supérieure à l'ancienne capacité." et (atreserve
) "La réallocation invalide toutes les références, pointeurs et itérateurs faisant référence aux éléments de la séquence."La norme garantit même votre premier exemple d'être en sécurité. Citant C ++ 11
[sequence.reqmts]
Ainsi, même si ce n'est pas vraiment trivial, l'implémentation doit garantir qu'elle n'invalidera pas la référence lors de l'exécution du
push_back
.la source
t
, la seule question est de savoir si avant ou après la copie. Votre dernière phrase est certainement fausse.t
remplit les conditions préalables énumérées, le comportement décrit est garanti. Une implémentation n'est pas autorisée à invalider une condition préalable, puis à l'utiliser comme excuse pour ne pas se comporter comme spécifié.for_each
est nécessaire pour ne pas invalider les itérateurs. Je ne peux pas trouver de référence pourfor_each
, mais je vois sur certains algorithmes du texte comme "op et binary_op n'invalideront pas les itérateurs ou les sous-plages".Il n'est pas évident que le premier exemple soit sûr, car l'implémentation la plus simple de
push_back
serait d'abord de réallouer le vecteur, si nécessaire, puis de copier la référence.Mais au moins, cela semble être sûr avec Visual Studio 2010. Son implémentation de
push_back
fait une gestion spéciale du cas lorsque vous repoussez un élément dans le vecteur. Le code est structuré comme suit:la source
Ce n'est pas une garantie de la norme, mais comme un autre point de données, il
v.push_back(v[0])
est sans danger pour la libc ++ de LLVM .std::vector::push_back
Appels de libc ++__push_back_slow_path
quand il a besoin de réallouer de la mémoire:la source
__swap_out_circular_buffer
, auquel cas cette implémentation est en effet sûre.__swap_out_circular_buffer
. (J'ai ajouté quelques commentaires pour le noter.)La première version n'est certainement PAS sûre:
de la section 17.6.5.9
Notez qu'il s'agit de la section sur les courses de données, à laquelle les gens pensent normalement en conjonction avec le threading ... mais la définition réelle implique des relations «se produit avant», et je ne vois aucune relation d'ordre entre les multiples effets secondaires de
push_back
in play ici, à savoir que l'invalidation de référence ne semble pas être définie comme ordonnée par rapport à la construction par copie du nouvel élément de queue.la source
v[0]
n'est pas un itérateur, de même,push_back()
ne prend pas d'itérateur. Donc, du point de vue des juristes linguistiques, votre argument est nul. Désolé. Je sais que la plupart des itérateurs sont des pointeurs, et le point d'invalider un itérateur est à peu près le même que pour les références, mais la partie de la norme que vous citez n'est pas pertinente pour la situation actuelle.x.push_back(x[0])
c'est SÛR.C'est complètement sûr.
Dans votre deuxième exemple, vous avez
ce qui n'est pas nécessaire car si le vecteur sort de sa taille, cela impliquera l'extension
reserve
.Vector est responsable de ce truc, pas vous.
la source
Les deux sont sûrs car push_back copiera la valeur, pas la référence. Si vous stockez des pointeurs, c'est toujours sûr en ce qui concerne le vecteur, mais sachez simplement que vous aurez deux éléments de votre vecteur pointant vers les mêmes données.
Les implémentations de push_back doivent donc garantir qu'une copie de
v[0]
est insérée. Par contre-exemple, en supposant une implémentation qui se réallouerait avant la copie, elle n'ajouterait pas assurément une copie dev[0]
et, en tant que telle, violerait les spécifications.la source
push_back
redimensionnera cependant également le vecteur, et dans une implémentation naïve, cela invalidera la référence avant que la copie ne se produise. Donc, à moins que vous ne puissiez étayer cela par une citation de la norme, je considérerai que c'est faux.push_back
copiera la valeur dans le vecteur; mais (pour autant que je puisse voir) cela pourrait se produire après la réallocation, à quel point la référence à partir de laquelle il essaie de copier n'est plus valide.push_back
reçoit son argument par référence .À partir du 23.3.6.5/1:
Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
Comme nous l'insérons à la fin, aucune référence ne sera invalidée si le vecteur n'est pas redimensionné. Donc, si le vecteur est
capacity() > size()
alors il est garanti de fonctionner, sinon il est garanti qu'il s'agit d'un comportement indéfini.la source
references
partie de la citation.push_back
).