Wiederverwendung eines bewegten Containers?

83

Was ist der richtige Weg, um einen bewegten Container wiederzuverwenden?

std::vector<int> container;
container.push_back(1);
auto container2 = std::move(container);

// ver1: Do nothing
//container2.clear(); // ver2: "Reset"
container = std::vector<int>() // ver3: Reinitialize

container.push_back(2);
assert(container.size() == 1 && container.front() == 2);

Nach dem, was ich im C ++ 0x-Standardentwurf gelesen habe; ver3 scheint der richtige Weg zu sein, da sich ein Objekt nach dem Verschieben in a befindet

"Sofern nicht anders angegeben, werden solche weggezogenen Objekte in einen gültigen, aber nicht spezifizierten Zustand versetzt."

Ich habe nie einen Fall gefunden, in dem es "anders angegeben" ist.

Ich finde ver3 zwar ein bisschen umständlich und hätte ver1 sehr bevorzugt, obwohl vec3 eine zusätzliche Optimierung ermöglichen kann, aber andererseits leicht zu Fehlern führen kann.

Ist meine Annahme richtig?

Ronag
quelle
4
Sie können einfach anrufen clear, da es keine Voraussetzungen hat (und somit kein Vertrauen in den Zustand des Objekts).
Nicol Bolas
@Nicol: Nehmen wir an, es gab eine std::vectorImplementierung, die einen Zeiger auf seine Größe gespeichert hat (scheint albern, aber legal). Wenn Sie sich von diesem Vektor entfernen, bleibt der Zeiger möglicherweise NULL. Danach schlägt der clearFehler fehl. operator=könnte auch scheitern.
Ben Voigt
9
@ Ben: Ich denke, das würde den "gültigen" Teil von "gültig, aber nicht spezifiziert" verletzen.
ildjarn
1
@ildjarn: Ich dachte, es bedeutet nur, dass es sicher ist, den Destruktor auszuführen.
Ben Voigt
Ich denke die Frage ist was ist "gültig"?
Ronag

Antworten:

97

Aus Abschnitt 17.3.26 der Spezifikation "gültiger, aber nicht spezifizierter Zustand":

Ein Objektstatus, der nicht angegeben wird, außer dass die Invarianten des Objekts erfüllt sind und Operationen für das Objekt sich wie für seinen Typ angegeben verhalten. [Beispiel: Wenn sich ein Objekt xvom Typ std::vector<int>in einem gültigen, aber nicht angegebenen Status befindet, x.empty()kann es bedingungslos aufgerufen und x.front()aufgerufen werden Nur wenn x.empty()false zurückgegeben wird. - Beispiel beenden]

Daher ist das Objekt live. Sie können jeden Vorgang ausführen, für den keine Vorbedingung erforderlich ist (es sei denn, Sie überprüfen die Vorbedingung zuerst).

clearhat zum Beispiel keine Voraussetzungen. Und es wird das Objekt in einen bekannten Zustand zurückversetzen. Also einfach löschen und wie gewohnt verwenden.

Nicol Bolas
quelle
Wo im Standard kann ich über "Voraussetzungen" für z. B. std :: vector-Methoden lesen?
Ronag
1
@ronag: §23.2 enthält Tabellen, in denen diese aufgelistet sind.
Grizzly
2
Ich fand Folgendes interessant, open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html , sie schreiben "Container können leerer als leer sein".
Ronag
4
@ronag: 1) Wenn sich der Container in einem gültigen Zustand befindet, clearist der Aufruf gültig. 2) Während der Behälter war in einem unbestimmten Zustand, Aufruf clearsetzt den Behälter in einen bestimmten Zustand , weil es Nachbedingungen in der Norm (§23.2.3 Tabelle 100) beauftragt hat. std::vector<T>hat eine Klasseninvariante, push_back()die immer gültig ist (solange sie Tist CopyInsertable).
ildjarn
1
@ronag: open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html zitierte einen der Kommentare des nationalen Gremiums zum Zitat "leerer als leer". Der Kommentar der nationalen Stelle war falsch. N3241 hat einen solchen Zustand nicht vorgeschlagen. Wenn eine Implementierung eines std :: -Containers einen Status "leerer als leer" hat, der sich aus einem Verschieben ergibt, muss dieser Status ein gültiger Status sein (dh Sie können mit diesem Objekt alles tun, was keine Voraussetzungen erfordert).
Howard Hinnant
11

Das Objekt befindet sich in einem gültigen, aber nicht definierten Zustand. Dies bedeutet im Grunde, dass der genaue Zustand des Objekts zwar nicht garantiert ist, aber gültig ist und als solche garantiert, dass Mitgliedsfunktionen (oder Nicht-Mitgliedsfunktionen) funktionieren, solange sie nicht abhängig sind auf dem Objekt mit einem bestimmten Zustand.

Die clear()Member-Funktion hat keine Voraussetzungen für den Status des Objekts (außer dass sie natürlich gültig ist) und kann daher bei verschobenen Objekten aufgerufen werden. Andererseits front()hängt zum Beispiel davon ab, dass der Container nicht leer ist und daher nicht aufgerufen werden kann, da nicht garantiert wird, dass er nicht leer ist.

Daher sollten sowohl ver2 als auch ver3 in Ordnung sein.

Grizzly
quelle
Ein Vektor wird immer leer sein, aber das gilt nicht für den allgemeinen Fall (IE-Array)
Mooing Duck
"Ein Vektor wird immer leer sein", worauf stützen Sie sich?
Ronag
1
@ronag: Ich meinte natürlich ver2 und ver3 (wie aus dem Text ersichtlich sein sollte, korrigierte diesen Tippfehler
Grizzly
Interessanterweise werden die Voraussetzungen für front()nur für std::arrayund auch dort nicht in der Tabelle angegeben.
Ben Voigt
1
@Ben: §23.2.3 Tabelle 100 besagt, dass die Betriebssemantik von front()are ist *a.begin(), §23.2.1 / 6 sagt " Wenn der Container leer ist, dannbegin() == end() " und §24.2.1 / 5 sagt " Die Bibliothek nimmt niemals an, dass Vergangenheit- Die Endwerte sind dereferenzierbar. " Folglich denke ich, dass die Voraussetzungen dafür front()abgeleitet werden können, obwohl dies sicherlich klarer gemacht werden könnte.
ildjarn
-8

Ich glaube nicht, dass Sie mit einem verschobenen Objekt ALLES tun können (außer es zu zerstören).

Können Sie nicht swapstattdessen alle Vorteile des Umzugs nutzen, sondern den Container in einem bekannten Zustand belassen?

Ben Voigt
quelle
+1. Swap ist eine gute Idee, obwohl es nicht in allen Fällen funktioniert, z. B. funktioniert die Verwendung von Auto nicht. Vielleicht könnte ein safe_move, der intern Swap verwendet, eine Idee sein?
Ronag
5
Es ist ein lebendes Objekt, und Sie können alle Funktionen verwenden, die keine Voraussetzungen haben (außer Invarianten)
Mooing Duck
Die primäre Vorlage für std::swapenthält zwei Verschiebungszuweisungen, wobei die Ziele dieser Zuweisungen von Werten verschoben werden. Das zählt für mich als "etwas mit einem verschobenen Objekt tun"
Caleth