Wann sollte std::move für einen Funktionsrückgabewert verwendet werden? [duplicate]

Lesezeit: 9 Minuten

Wann sollte stdmove fur einen Funktionsruckgabewert verwendet werden duplicate
Benutzer2015453

In diesem Fall

struct Foo {};
Foo meh() {
  return std::move(Foo());
}

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


1646941212 936 Wann sollte stdmove fur einen Funktionsruckgabewert verwendet werden duplicate
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

1646941213 150 Wann sollte stdmove fur einen Funktionsruckgabewert verwendet werden duplicate
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).

1646941213 803 Wann sollte stdmove fur einen Funktionsruckgabewert verwendet werden duplicate
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:

Foo meh() {
    Foo foo(args);
    foo.method(xyz);
    bar();
    return foo;
}

in diesen Code:

void meh(Foo *retval) {
   new (retval) Foo(arg);
   retval->method(xyz);
   bar();
}

was den Aufruf des Kopierkonstruktors und des Destruktors von vermeidet Foo.


Jahr-2017-C++-Compiler wie GCC 6.3.0 sind nicht optimieren können diese Codes:

Foo meh_value() {
    Foo foo(args);
    Foo retval(foo);
    return retval;
}

Foo meh_pointer() {
    Foo *foo = get_foo();
    Foo retval(*foo);
    delete foo;
    return retval;
}

in diese Codes:

Foo meh_value() {
    Foo foo(args);
    Foo retval(std::move(foo));
    return retval;
}

Foo meh_pointer() {
    Foo *foo = get_foo();
    Foo retval(std::move(*foo));
    delete foo;
    return retval;
}

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

Wann sollte stdmove fur einen Funktionsruckgabewert verwendet werden duplicate
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.

Siehe hier.

  • @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

988820cookie-checkWann sollte std::move für einen Funktionsrückgabewert verwendet werden? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy