vector<int> v;
v.push_back(1);
v.push_back(v[0]);
Wenn der zweite push_back eine Neuzuweisung verursacht, ist der Verweis auf die erste Ganzzahl im Vektor nicht mehr gültig. Das ist also nicht sicher?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
Das macht es sicher?
push_back
. Ein anderes Poster bemerkte einen Fehler darin , dass es den von Ihnen beschriebenen Fall nicht richtig behandelte. Soweit ich das beurteilen kann, hat niemand anders argumentiert, dass dies kein Fehler war. Das heißt nicht, dass dies ein schlüssiger Beweis ist, sondern nur eine Beobachtung.Antworten:
Es sieht so aus, als ob http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 dieses Problem (oder etwas sehr Ähnliches) als potenziellen Fehler in der Norm angesprochen hat:
Die vorgeschlagene Lösung war, dass dies kein Mangel war:
quelle
v.insert(v.begin(), v[2]);
kann keine Neuzuweisung auslösen. Wie beantwortet dies die Frage?Ja, es ist sicher, und Standardbibliotheksimplementierungen springen durch die Rahmen, um dies zu erreichen.
Ich glaube, dass Implementierer diese Anforderung irgendwie auf 23.2 / 11 zurückführen, aber ich kann nicht herausfinden, wie und ich kann auch nichts Konkreteres finden. Das Beste, was ich finden kann, ist dieser Artikel:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
Die Überprüfung der Implementierungen von libc ++ und libstdc ++ zeigt, dass sie auch sicher sind.
quelle
vec.insert(vec.end(), vec.begin(), vec.end());
?vector.push_back
gibt es anders an. "Verursacht eine Neuzuweisung, wenn die neue Größe größer als die alte Kapazität ist." und (atreserve
) "Neuzuweisung macht alle Referenzen, Zeiger und Iteratoren ungültig, die sich auf die Elemente in der Sequenz beziehen."Der Standard garantiert, dass auch Ihr erstes Beispiel sicher ist. Zitieren von C ++ 11
[sequence.reqmts]
Auch wenn dies nicht gerade trivial ist, muss die Implementierung sicherstellen, dass die Referenz bei der Ausführung nicht ungültig wird
push_back
.quelle
t
, die einzige Frage ist, ob vor oder nach dem Erstellen der Kopie. Dein letzter Satz ist sicherlich falsch.t
die aufgeführten Voraussetzungen erfüllt sind, ist das beschriebene Verhalten garantiert. Eine Implementierung darf eine Vorbedingung nicht ungültig machen und diese dann als Entschuldigung dafür verwenden, sich nicht wie angegeben zu verhalten.for_each
erforderlich ist, um die Iteratoren nicht ungültig zu machen. Ich kann keine Referenz für findenfor_each
, aber ich sehe auf einigen Algorithmen Text wie "op und binary_op sollen Iteratoren oder Unterordnungen nicht ungültig machen".Es ist nicht offensichtlich, dass das erste Beispiel sicher ist, da die einfachste Implementierung
push_back
darin besteht, den Vektor bei Bedarf zuerst neu zuzuweisen und dann die Referenz zu kopieren.Zumindest scheint es mit Visual Studio 2010 sicher zu sein. Durch die Implementierung von
push_back
wird der Fall speziell behandelt, wenn Sie ein Element im Vektor zurückschieben. Der Code ist wie folgt aufgebaut:quelle
Dies ist keine Garantie des Standards, aber als weiterer Datenpunkt
v.push_back(v[0])
für libc ++ von LLVM sicher .Die
std::vector::push_back
Aufrufe von libc ++,__push_back_slow_path
wenn Speicher neu zugewiesen werden muss:quelle
__swap_out_circular_buffer
. In diesem Fall ist diese Implementierung tatsächlich sicher.__swap_out_circular_buffer
. (Ich habe einige Kommentare hinzugefügt, um das zu beachten.)Die erste Version ist definitiv NICHT sicher:
ab Abschnitt 17.6.5.9
Beachten Sie, dass dies der Abschnitt über Datenrennen ist, an den die Leute normalerweise im Zusammenhang mit Threading denken ... aber die eigentliche Definition beinhaltet "passiert vor" -Beziehungen, und ich sehe keine Ordnungsbeziehung zwischen den mehreren Nebenwirkungen von
push_back
in spielen hier, nämlich die Referenzinvalidierung scheint nicht als geordnet in Bezug auf das Kopieren des neuen Schwanzelements definiert zu sein.quelle
v[0]
ist kein Iterator, ebenfallspush_back()
kein Iterator. Aus Sicht eines Sprachrechtsanwalts ist Ihr Argument also nichtig. Es tut uns leid. Ich weiß, dass die meisten Iteratoren Zeiger sind und der Punkt, an dem ein Iterator ungültig gemacht wird, fast der gleiche ist wie bei Referenzen, aber der Teil des Standards, den Sie zitieren, ist für die jeweilige Situation irrelevant.x.push_back(x[0])
ist sicher.Es ist völlig sicher.
In Ihrem zweiten Beispiel haben Sie
Dies wird nicht benötigt, da ein Vektor, der seine Größe überschreitet, das impliziert
reserve
.Vector ist für dieses Zeug verantwortlich, nicht Sie.
quelle
Beide sind sicher, da push_back den Wert und nicht die Referenz kopiert. Wenn Sie Zeiger speichern, ist dies für den Vektor immer noch sicher, aber Sie müssen nur wissen, dass zwei Elemente Ihres Vektors auf dieselben Daten zeigen.
Implementierungen von push_back müssen daher sicherstellen, dass eine Kopie von
v[0]
eingefügt wird. Wenn beispielsweise eine Implementierung angenommen wird, die vor dem Kopieren neu zugewiesen wird, wird eine Kopiev[0]
der Spezifikationen nicht sicher angehängt und verstößt somit gegen die Spezifikationen.quelle
push_back
Die Größe des Vektors wird jedoch ebenfalls geändert , und in einer naiven Implementierung wird dadurch die Referenz ungültig, bevor das Kopieren erfolgt. Wenn Sie dies nicht durch ein Zitat aus dem Standard belegen können, halte ich es für falsch.push_back
kopiert den Wert in den Vektor; Aber (soweit ich sehen kann) kann dies nach der Neuzuweisung passieren. Ab diesem Zeitpunkt ist die Referenz, von der kopiert werden soll, nicht mehr gültig.push_back
erhält sein Argument durch Bezugnahme .Vom 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.
Da wir am Ende einfügen, werden keine Referenzen ungültig, wenn die Größe des Vektors nicht geändert wird. Wenn der Vektor also
capacity() > size()
funktioniert, funktioniert er garantiert, andernfalls handelt es sich garantiert um undefiniertes Verhalten.quelle
references
Teil des Zitats.push_back
).