Ich bin mir ziemlich sicher, dass der Umzug unnötig ist, weil die neu erstellten Foo wird ein X-Wert sein.
Aber was in solchen Fällen?
struct Foo {};
Foo meh() {
Foo foo;
//do something, but knowing that foo can safely be disposed of
//but does the compiler necessarily know it?
//we may have references/pointers to foo. how could the compiler know?
return std::move(foo); //so here the move is needed, right?
}
Da ist der Umzug nötig, nehme ich an?
Wenn Sie Visual Studio verwenden.
– R.Martinho Fernandes
13. Februar 2013 um 14:57 Uhr
Nur zur Info im zweiten Fall, Sie kann nicht keine verwendbaren Verweise/Zeiger auf foo mehr haben, wenn Sie von der Funktion zurückkehren.
– R.Martinho Fernandes
13. Februar 2013 um 14:57 Uhr
Was machst du mit dem zurückgegebenen Wert? Foo f = meh(); hat mit (N)RVO bereits in C++98 gearbeitet.
– Bo Persson
13. Februar 2013 um 15:00 Uhr
Ich frage mich, ob der explizite Aufruf von std::move NRVO verhindert …
– Ding
13. Februar 2013 um 15:09 Uhr
std::move ist eine Identitätsoperation. Es bringt eigentlich nie was. Es ist nur ein Marker für rvalues. Wenn der Compiler den Bewegungskonstruktor von hat Foo Auf der Hand kann es sehen, ob es beobachtbare Auswirkungen hat, und darüber entscheiden. Wenn es keine beobachtbaren Auswirkungen hat, wie können Sie den Unterschied feststellen?
– R.Martinho Fernandes
13. Februar 2013 um 15:18 Uhr
Steve Jessop
Im Falle des return std::move(foo); der move ist wegen 12.8/32 überflüssig:
Wenn die Kriterien für die Elision eines Kopiervorgangs erfüllt sind oder erfüllt wären, abgesehen von der Tatsache, dass das Quellobjekt ein Funktionsparameter ist und das zu kopierende Objekt durch einen Lvalue gekennzeichnet ist, ist die Überladungsauflösung zur Auswahl des Konstruktors für die Kopie zunächst so ausgeführt, als ob das Objekt durch einen rvalue gekennzeichnet wäre.
return foo; handelt es sich um einen Fall von NRVO, so ist das Weglassen von Kopien zulässig. foo ist ein lvalue. Also wählt der Konstruktor für das “Kopieren” aus foo zum Rückgabewert von meh muss der Bewegungskonstruktor sein, falls vorhanden.
Hinzufügen move hat jedoch einen potenziellen Effekt: Es verhindert, dass der Zug eliminiert wird, weil return std::move(foo); ist nicht NRVO-berechtigt.
Soweit ich weiß, legt das 12.8/32 fest nur Bedingungen, unter denen eine Kopie eines lvalue durch eine Verschiebung ersetzt werden kann. Dem Compiler ist es im Allgemeinen nicht gestattet, nach dem Kopieren (z. B. mit DFA) zu erkennen, dass ein lvalue unbenutzt ist, und die Änderung von sich aus vorzunehmen. Ich gehe hier davon aus, dass es einen beobachtbaren Unterschied zwischen den beiden gibt – wenn das beobachtbare Verhalten dasselbe ist, gilt die “Als-ob”-Regel.
Um also die Frage im Titel zu beantworten, verwenden Sie std::move auf einen Rückgabewert, wenn Sie möchten, dass er verschoben wird und er sowieso nicht verschoben wird. Das ist:
Sie möchten, dass es verschoben wird, und
es ist ein lvalue, und
es ist nicht für das Entfernen von Kopien geeignet, und
es ist nicht der Name eines By-Value-Funktionsparameters.
Wenn man bedenkt, dass dies ziemlich fummelig ist und Bewegungen sind meistens billig, möchten Sie vielleicht sagen, dass Sie dies in Nicht-Vorlagencode ein wenig vereinfachen können. Verwenden std::move wann:
Sie möchten, dass es verschoben wird, und
es ist ein lvalue, und
Sie können sich darüber keine Sorgen machen.
Indem Sie die vereinfachten Regeln befolgen, opfern Sie einige Zugausfälle. Für Typen wie std::vector die billig zu bewegen sind, werden Sie wahrscheinlich nie bemerken (und wenn Sie es bemerken, können Sie optimieren). Für Typen wie std::array die teuer zu bewegen sind, oder bei Vorlagen, bei denen Sie keine Ahnung haben, ob die Bewegungen billig sind oder nicht, werden Sie sich eher Sorgen darüber machen müssen.
C++: Knochen einfach. Deutlich.
– Leichtigkeitsrennen im Orbit
11. November 2013 um 18:24 Uhr
C++: Wer nicht lacht, wird weinen.
– Mackenir
4. August 2015 um 14:14 Uhr
Was ist mit dem Versuch, Variablen zurückzugeben, die als deklariert sind? std::unique_ptr<Derived> wenn die Funktion als Rückgabe deklariert wird std::unique_ptr<Base>? In gcc und mingw-w64 funktioniert es einfach, aber Vanilla mingw (basiert auf gcc 4.9.3, targeting i686-pc-cygwin) braucht std::move(x) kompilieren.
– rr-
12. Oktober 2015 um 7:01 Uhr
@rr: nicht sicher. Ich würde erwarten, die zu brauchen std::move In diesem Fall (weil die Typen nicht übereinstimmen und das Entfernen von Kopien daher vom Tisch ist), könnte ich jedoch etwas übersehen. Die Tatsache, dass es zwischen 32-Bit- und 64-Bit-Mingw unterscheidet, ist seltsam. Mir fällt auf Anhieb kein Grund ein, warum der Compiler oder die Plattformautoren dies beabsichtigen würden.
– Steve Jessop
12. Oktober 2015 um 9:33 Uhr
Danke, das macht Sinn. Nur um die Dinge klarzustellen, mingw-w64 ist nicht nur eine 64-Bit-Version von mingw – es ist ein Fork, der einige wesentliche Updates einführt.
– rr-
12. Oktober 2015 um 9:48 Uhr
Der Umzug ist in beiden Fällen unnötig. Im zweiten Fall std::move ist überflüssig, da Sie eine lokale Variable als Wert zurückgeben, und der Compiler versteht, dass, da Sie diese lokale Variable nicht mehr verwenden werden, sie verschoben und nicht kopiert werden kann.
Gebrauch von std::move gilt als schädlich, kann Elision verhindern
– Seth Carnegie
13. Februar 2013 um 15:20 Uhr
Korrektur: Verwendung von std::move für Rückgabewerte gilt als schädlich, kann Elision verhindern
– Andreas Magnusson
13. Februar 2013 um 15:31 Uhr
Wenn sich der Rückgabeausdruck bei einem Rückgabewert direkt auf den Namen eines lokalen lvalue bezieht (dh an dieser Stelle ein xvalue), besteht keine Notwendigkeit für den std::move. Auf der anderen Seite, wenn der Rückgabeausdruck ist nicht der Bezeichner, er wird nicht automatisch verschoben, also bräuchten Sie zum Beispiel den Explicit std::move in diesem Fall:
T foo(bool which) {
T a = ..., b = ...;
return std::move(which? a : b);
// alternatively: return which? std::move(a), std::move(b);
}
Wenn Sie eine benannte lokale Variable oder einen temporären Ausdruck direkt zurückgeben, sollten Sie das explizite vermeiden std::move. Der Compiler Muss (und wird in Zukunft) in diesen Fällen automatisch verschieben und hinzufügen std::move kann sich auf andere Optimierungen auswirken.
Es sollte beachtet werden, dass, wenn einer der Parameter der Ternärdatei direkt erstellt wird, er direkt in die Rückgabevariable eingebaut wird. Eine Bewegung darauf wird dies jedoch verhindern. Dies macht die zweite Alternative flexibler – man verschiebt nur das genannte arg.
– Benutzer362515
13. Oktober 2016 um 12:40 Uhr
Yakk – Adam Nevraumont
Es gibt viele Antworten darauf, wann es nicht verschoben werden sollte, aber die Frage lautet: “Wann sollte es verschoben werden?”
Hier ist ein erfundenes Beispiel dafür, wann es verwendet werden sollte:
std::vector<int> append(std::vector<int>&& v, int x) {
v.push_back(x);
return std::move(v);
}
dh wenn Sie eine Funktion haben, die eine Rvalue-Referenz annimmt, sie ändert und dann eine Kopie davon zurückgibt. (In c++20 ändert sich das Verhalten hier) Nun, in der Praxis ist dieses Design fast immer besser:
std::vector<int> append(std::vector<int> v, int x) {
v.push_back(x);
return v;
}
wodurch Sie auch Nicht-Rvalue-Parameter verwenden können.
Wenn Sie eine Rvalue-Referenz innerhalb einer Funktion haben, die Sie durch Verschieben zurückgeben möchten, müssen Sie sie aufrufen std::move. Wenn Sie eine lokale Variable haben (sei es ein Parameter oder nicht), geben Sie sie implizit zurück moves (und diese implizite Bewegung kann weggelassen werden, während eine explizite Bewegung dies nicht kann). Wenn Sie eine Funktion oder Operation haben, die lokale Variablen verwendet und eine Referenz auf diese lokale Variable zurückgibt, müssen Sie dies tun std::move um in Bewegung zu kommen (als Beispiel die Trinäre ?: Operator).
Atomsymbol
Ein C++-Compiler kann kostenlos verwendet werden std::move(foo):
wenn das bekannt ist foo ist am Ende seiner Lebensdauer, und
die implizite Verwendung von std::move hat keine Auswirkung auf die Semantik des C++-Codes außer den semantischen Effekten, die von der C++-Spezifikation zugelassen werden.
Es hängt von den Optimierungsmöglichkeiten des C++-Compilers ab, ob er welche Transformationen berechnen kann f(foo); foo.~Foo(); zu f(std::move(foo)); foo.~Foo(); in Bezug auf die Leistung oder den Speicherverbrauch rentabel sind, während die C++-Spezifikationsregeln eingehalten werden.
Konzeptionell Apropos, Jahr-2017-C++-Compiler wie GCC 6.3.0 sind optimieren können dieser Code:
was bedeutet, dass ein Programmierer des Jahres 2017 solche Optimierungen explizit angeben muss.
@MM Ich interessiere mich in diesem Fall nicht so sehr für die C++-Terminologie. Der Ausdruck „call to std::move“ in der Antwort hat die gleiche Bedeutung wie „use of std::move“.
– Atomsymbol
27. Februar 2017 um 22:59 Uhr
@MM Haben Sie weitere Vorschläge zur Verbesserung der Antwort?
– Atomsymbol
27. Februar 2017 um 23:03 Uhr
@atomsymbol Dies ist eine großartige informative Antwort, die zu den vorhandenen hinzukommt. Ich weiß nicht, worum es bei der Aufregung geht.
– Avin Kavisch
18. Juni 2020 um 4:01 Uhr
Robert Allan Hennigan Leahy
std::move ist völlig unnötig, wenn Sie von einer Funktion zurückkehren, und gerät wirklich in den Bereich von Ihnen – dem Programmierer – der versucht, Dinge zu babysitten, die Sie dem Compiler überlassen sollten.
Was passiert, wenn Sie std::move etwas aus einer Funktion, das keine lokale Variable dieser Funktion ist? Sie können sagen, dass Sie niemals solchen Code schreiben werden, aber was passiert, wenn Sie Code schreiben, der in Ordnung ist, und ihn dann umgestalten und geistesabwesend nicht ändern std::move. Sie werden Spaß daran haben, diesen Fehler aufzuspüren.
Der Compiler hingegen ist zu solchen Fehlern meist nicht in der Lage.
Außerdem: Wichtig zu beachten, dass die Rückgabe einer lokalen Variablen aus einer Funktion funktioniert nicht unbedingt einen rvalue erstellen oder Move-Semantik verwenden.
@MM Ich interessiere mich in diesem Fall nicht so sehr für die C++-Terminologie. Der Ausdruck „call to std::move“ in der Antwort hat die gleiche Bedeutung wie „use of std::move“.
– Atomsymbol
27. Februar 2017 um 22:59 Uhr
@MM Haben Sie weitere Vorschläge zur Verbesserung der Antwort?
– Atomsymbol
27. Februar 2017 um 23:03 Uhr
@atomsymbol Dies ist eine großartige informative Antwort, die zu den vorhandenen hinzukommt. Ich weiß nicht, worum es bei der Aufregung geht.
– Avin Kavisch
18. Juni 2020 um 4:01 Uhr
9888200cookie-checkWann sollte std::move für einen Funktionsrückgabewert verwendet werden? [duplicate]yes
Wenn Sie Visual Studio verwenden.
– R.Martinho Fernandes
13. Februar 2013 um 14:57 Uhr
Nur zur Info im zweiten Fall, Sie kann nicht keine verwendbaren Verweise/Zeiger auf foo mehr haben, wenn Sie von der Funktion zurückkehren.
– R.Martinho Fernandes
13. Februar 2013 um 14:57 Uhr
Was machst du mit dem zurückgegebenen Wert?
Foo f = meh();
hat mit (N)RVO bereits in C++98 gearbeitet.– Bo Persson
13. Februar 2013 um 15:00 Uhr
Ich frage mich, ob der explizite Aufruf von std::move NRVO verhindert …
– Ding
13. Februar 2013 um 15:09 Uhr
std::move
ist eine Identitätsoperation. Es bringt eigentlich nie was. Es ist nur ein Marker für rvalues. Wenn der Compiler den Bewegungskonstruktor von hatFoo
Auf der Hand kann es sehen, ob es beobachtbare Auswirkungen hat, und darüber entscheiden. Wenn es keine beobachtbaren Auswirkungen hat, wie können Sie den Unterschied feststellen?– R.Martinho Fernandes
13. Februar 2013 um 15:18 Uhr