Kopieren mit der “return” -statement vermeiden

Ich habe eine sehr grundlegende Frage in C ++. Wie vermeide ich das Kopieren eines Objekts?

Hier ist ein Beispiel :

std::vector test(const unsigned int n) { std::vector x; for (unsigned int i = 0; i < n; ++i) { x.push_back(i); } return x; } 

Wenn ich verstehe, wie C ++ funktioniert, erzeugt diese function 2 Vektoren: den lokalen (x) und die Kopie von x, die zurückgegeben wird. Gibt es eine Möglichkeit, die Kopie zu vermeiden? (und ich möchte keinen pointers auf ein Objekt zurückgeben, sondern das Objekt selbst)


Was wäre die Syntax dieser function mit “move semantics” (die in den Kommentaren angegeben wurde)?

Dieses Programm kann die benannte Rückgabewertoptimierung (NRVO) nutzen. Siehe hier: http://en.wikipedia.org/wiki/Copy_elision

In C ++ 11 gibt es Move-Konstruktoren und -Aufgaben, die auch billig sind. Sie können ein Tutorial hier lesen: http://thbecker.net/articles/rvalue_references/section_01.html

Es scheint einige Verwirrung darüber zu bestehen, wie die RVO (Return Value Optimization) funktioniert.

Ein einfaches Beispiel:

 #include  struct A { int a; int b; int c; int d; }; A create(int i) { A a = {i, i+1, i+2, i+3 }; std::cout < < &a << "\n"; return a; } int main(int argc, char*[]) { A a = create(argc); std::cout << &a << "\n"; } 

Und seine Ausgabe bei ideone :

 0xbf928684 0xbf928684 

Überraschend?

Das ist der Effekt von RVO: Das zurückzugebende Objekt wird direkt im Aufrufer aufgebaut.

Wie ?

Traditionell reserviert der Aufrufer (hier main ) etwas Platz auf dem Stapel für den Rückgabewert: den Return-Slot ; Der Angerufene (hier create ) wird (irgendwie) die Adresse des Rückgabeschlitzes übergeben, in den er seinen Rückgabewert kopiert. Der Angerufene reserviert dann wie bei jeder anderen lokalen Variablen seinen eigenen Platz für die lokale Variable, in der er das Ergebnis erstellt, und kopiert sie dann bei der Rückgabeanweisung in den return .

RVO wird ausgetriggers, wenn der Compiler aus dem Code ableitet, dass die Variable direkt in den Return-Slot mit äquivalenter Semantik (der as-if-Regel) konstruiert werden kann.

Beachten Sie, dass dies eine allgemeine Optimierung ist, die vom Standard explizit in die weiße Liste aufgenommen wird und der Compiler sich keine Gedanken über mögliche Nebenwirkungen des Konstruktors für Kopieren (oder Verschieben) machen muss.

Wann ?

Der Compiler verwendet am wahrscheinlichsten einfache Regeln wie:

 // 1. works A unnamed() { return {1, 2, 3, 4}; } // 2. works A unique_named() { A a = {1, 2, 3, 4}; return a; } // 3. works A mixed_unnamed_named(bool b) { if (b) { return {1, 2, 3, 4}; } A a = {1, 2, 3, 4}; return a; } // 4. does not work A mixed_named_unnamed(bool b) { A a = {1, 2, 3, 4}; if (b) { return {4, 3, 2, 1}; } return a; } 

Im letzteren Fall (4) kann die Optimierung nicht angewendet werden, wenn A zurückgegeben wird, da der Compiler kein a im Rückgabeschlitz erstellen kann, da er es möglicherweise für etwas anderes benötigt (abhängig von der booleschen Bedingung b ).

Eine einfache Faustregel lautet also:

RVO sollte angewendet werden, wenn vor der return kein anderer Kandidat für den Rückgabeschlitz angegeben wurde.

Die benannte Rückgabewert-Optimierung erledigt die Aufgabe für Sie, da der Compiler versucht, redundante Copy-Konstruktor- und Destruktor-Aufrufe zu eliminieren, während er sie verwendet.

 std::vector test(const unsigned int n){ std::vector x; return x; } ... std::vector y; y = test(10); 

mit Rückgabewertoptimierung:

  1. y ist erstellt
  2. x ist erstellt
  3. x wird in y zugewiesen
  4. x ist zerstört

(Falls du es selbst ausprobieren möchtest, um es besser zu verstehen, schau dir dieses Beispiel an )

oder noch besser, wie Matthieu M. darauf hingewiesen hat, wenn Sie den test innerhalb derselben Zeile aufrufen test in der y deklariert ist, können Sie auch die Konstruktion redundanter Objekte und redundanter Zuweisungen vermeiden ( x wird im Speicher erstellt, wo y gespeichert wird) ):

 std::vector y = test(10); 

Überprüfen Sie seine Antwort für ein besseres Verständnis dieser Situation (Sie werden auch feststellen, dass diese Art der Optimierung nicht immer angewendet werden kann).

ODER Sie könnten Ihren Code modifizieren, um die Referenz des Vektors an Ihre function zu übergeben, die semantisch korrekter wäre, während das Kopieren vermieden wird:

 void test(std::vector& x){ // use x.size() instead of n // do something with x... } ... std::vector y; test(y); 

Compiler können oft die zusätzliche Kopie für Sie optimieren (dies wird als Rückgabewertoptimierung bezeichnet ). Siehe https://isocpp.org/wiki/faq/ctors#return-by-value-optimization

Referenzieren würde funktionieren.

 Void(vector<> &x) { } 

Zuallererst könnten Sie Ihren Rückgabetyp als std :: vector deklarieren, und in diesem Fall wird anstelle einer Kopie ein Verweis zurückgegeben.

Sie könnten auch einen pointers definieren, einen pointers innerhalb Ihres Methodenkörpers erstellen und dann diesen pointers zurückgeben (oder eine Kopie dieses pointerss als korrekt).

Schließlich können viele C ++ – Compiler eine Rückgabewertoptimierung (http://en.wikipedia.org/wiki/Return_value_optimization) durchführen, wodurch das temporäre Objekt in einigen Fällen eliminiert wird.