11 Stimmen

Wie fügt man ein Duplikat-Element in einen Vektor ein?

Ich versuche, eine Kopie eines vorhandenen vector-Elements einzufügen, um es zu verdoppeln. Der folgende Code funktionierte in früheren Versionen, scheitert jedoch in Visual Studio 2010.

#include 
#include 

using namespace std;

int main(int argc, char* argv[])
{
   vector test;
   test.push_back(1);
   test.push_back(2);
   test.insert(test.begin(), test[0]);
   cout << test[0] << " " << test[1] << " " << test[2] << endl;
   return 0;
}

Die Ausgabe lautet -17891602 1 2, erwartet wird 1 1 2.

Ich habe herausgefunden, warum das passiert - der Vektor wird neu zugeordnet und die Referenz wird ungültig, bevor sie an die Einfügestelle kopiert wird. Das ältere Visual Studio führt die Dinge offenbar in einer anderen Reihenfolge aus, was beweist, dass ein mögliches Ergebnis eines undefinierten Verhaltens korrektes Funktionieren ist und auch beweist, dass man sich darauf nie verlassen sollte.

Ich habe zwei verschiedene Möglichkeiten gefunden, dieses Problem zu beheben. Eine besteht darin, reserve zu verwenden, um sicherzustellen, dass keine Neuordnung stattfindet:

   test.reserve(test.size() + 1);
   test.insert(test.begin(), test[0]);

Die andere besteht darin, eine Kopie aus der Referenz zu erstellen, sodass keine Abhängigkeit von der Gültigkeit der Referenz besteht:

template
T make_copy(const T & original)
{
    return original;
}

   test.insert(test.begin(), make_copy(test[0]));

Obwohl beide Lösungen funktionieren, fühlt sich keine wie eine natürliche Lösung an. Gibt es etwas, das ich übersehe?

4voto

devtk Punkte 1809

Das Problem ist, dass vector::insert einen Verweis auf einen Wert als zweiten Parameter und nicht einen Wert selbst benötigt. Du brauchst das Template nicht, um eine Kopie zu erstellen, verwende einfach einen Kopierkonstruktor, um ein anderes Objekt zu erstellen, das per Verweis übergeben wird. Diese Kopie bleibt auch dann gültig, wenn der Vektor neu dimensioniert wird.

#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
{
   vector test;
   test.push_back(1);
   test.push_back(2);
   test.insert(test.begin(), int(test[0]));
   cout << test[0] << " " << test[1] << " " << test[2] << endl;
   return 0;
}

1voto

David Brown Punkte 13000

Ich glaube, dies ist definiertes Verhalten. In §23.2.3 des C++-Standards von 2011 listet Tabelle 100 die Anforderungen an Sequenzcontainer auf, und es gibt einen Eintrag für diesen Fall. Es gibt das Beispiel:

a.insert(p,t)

wo a ein Wert von X ist, der ein Sequenzcontainer-Typ ist, der Elemente des Typs T enthält, p ein konstanter Iterator zu a ist, und t ein lvalue oder konstantes rvalue vom Typ X::value_type, also T.

Die Behauptung für diesen Ausdruck lautet:

Erfordert: T muss in X CopyInsertable sein. Für vector und deque muss T auch CopyAssignable sein.
Effekte: Fügt eine Kopie von t vor p ein.

Das einzige relevante Vektor-spezifische Zitat, das ich finden konnte, befindet sich in §23.3.6.5 Absatz 1:

Bemerkungen: Verursacht eine Neuzuweisung, wenn die neue Größe größer ist als die alte Kapazität. Wenn keine Neuzuweisung erfolgt, bleiben alle Iteratoren und Verweise vor dem Einfügepunkt gültig.

Obwohl dies erwähnt, dass der Vektor neu zugewiesen wird, wird keine Ausnahme von den vorherigen Anforderungen für insert bei Sequenzcontainern gemacht.

Was das Umgehen dieses Problems betrifft, stimme ich @EdChums Vorschlag zu, einfach eine Kopie des Elements zu machen und diese einzufügen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X