Meines Wissens nach kann der Compiler in C ++ 11, wenn Sie eine lokale Variable von einer Funktion nach Wert zurückgeben, diese Variable als R-Wert-Referenz behandeln und sie aus der Funktion 'verschieben', um sie zurückzugeben (if RVO / NRVO passiert natürlich nicht stattdessen).
Meine Frage ist, kann dies nicht vorhandenen Code brechen?
Betrachten Sie den folgenden Code:
#include <iostream>
#include <string>
struct bar
{
bar(const std::string& str) : _str(str) {}
bar(const bar&) = delete;
bar(bar&& other) : _str(std::move(other._str)) {other._str = "Stolen";}
void print() {std::cout << _str << std::endl;}
std::string _str;
};
struct foo
{
foo(bar& b) : _b(b) {}
~foo() {_b.print();}
bar& _b;
};
bar foobar()
{
bar b("Hello, World!");
foo f(b);
return std::move(b);
}
int main()
{
foobar();
return EXIT_SUCCESS;
}
Meine Gedanken waren, dass es einem Destruktor eines lokalen Objekts möglich sein würde, auf das Objekt zu verweisen, das implizit verschoben wird, und daher unerwartet ein "leeres" Objekt zu sehen. Ich habe versucht, dies zu testen (siehe http://ideone.com/ZURoeT ), aber ich habe das 'richtige' Ergebnis ohne das explizite std::move
in erhalten foobar()
. Ich vermute, das lag an NRVO, aber ich habe nicht versucht, den Code neu zu ordnen, um das zu deaktivieren.
Stimmt es, dass diese Transformation (die zu einem Verschieben der Funktion führt) implizit erfolgt und vorhandenen Code beschädigen kann?
UPDATE Hier ist ein Beispiel, das zeigt, wovon ich spreche. Die folgenden zwei Links beziehen sich auf denselben Code. http://ideone.com/4GFIRu - C ++ 03 http://ideone.com/FcL2Xj - C ++ 11
Wenn Sie sich die Ausgabe ansehen, ist es anders.
Ich denke, diese Frage wird nun, wurde dies beim Hinzufügen einer impliziten Verschiebung zum Standard berücksichtigt, und es wurde entschieden, dass es in Ordnung war, diese bahnbrechende Änderung hinzuzufügen, da diese Art von Code selten genug ist. Ich frage mich auch, ob Compiler in solchen Fällen warnen werden ...
Antworten:
Scott Meyers veröffentlichte in comp.lang.c ++ (August 2010) ein Problem, bei dem die implizite Generierung von Verschiebungskonstruktoren die Invarianten der C ++ 03-Klasse brechen könnte:
Hier ist das Problem, dass in C ++ 03
X
eine Invariante hatte, dass seinv
Mitglied immer 5 Elemente hatte.X::~X()
rechnete mit dieser Invariante, aber der neu eingeführte Verschiebungskonstruktor bewegte sich vonv
und setzte dadurch seine Länge auf Null.Dies hängt mit Ihrem Beispiel zusammen, da die gebrochene Invariante nur im
X
Destruktor des lokalen Objekts erkannt wird (wie Sie sagen, kann ein Destruktor eines lokalen Objekts auf das Objekt verweisen, das implizit verschoben wird, und daher unerwartet ein leeres Objekt sehen).C ++ 11 versucht, ein Gleichgewicht zwischen dem Brechen eines Teils des vorhandenen Codes und dem Bereitstellen nützlicher Optimierungen basierend auf Verschiebungskonstruktoren zu erreichen.
Das Komitee entschied zunächst, dass Verschiebungskonstruktoren und Verschiebungszuweisungsoperatoren vom Compiler generiert werden sollten, wenn sie nicht vom Benutzer bereitgestellt werden.
Dann wurde entschieden, dass dies tatsächlich Grund zur Besorgnis war, und die automatische Generierung von Verschiebungskonstruktoren und Verschiebungszuweisungsoperatoren wurde so eingeschränkt, dass es viel weniger wahrscheinlich, wenn auch nicht unmöglich ist, dass vorhandener Code beschädigt wird (z. B. explizit definierter Destruktor).
Es ist verlockend zu glauben, dass es ausreicht, die Generierung impliziter Verschiebungskonstruktoren zu verhindern, wenn ein benutzerdefinierter Destruktor vorhanden ist, dies ist jedoch nicht der Fall ( N3153 - Implizite Verschiebung muss für weitere Details ausgeführt werden).
In N3174 - Bewegen oder nicht bewegen Stroupstrup sagt:
quelle