rvalue-references/move Verständnisprobleme
-
Hi,
ich hab jetzt schon recht viel über rvalue-Referenzen gelesen, allerdings bin ich immer noch nicht ganz durchgestiegen:Angenommen ich habe in etwa folgendes:
class blub { /*zeug*/ public: X&& getWert() { return wert; } private: X wert; };
Wird jetzt irgendwo "X ergebnis = blubobjekt.getWert();" mache dann geht der Wert ja zu "ergebnis" über und ist in blub::wert nicht mehr vorhanden, richtig?
Was ist dann in blub::wert noch übrig, bzw. was passiert wenn ich nochmal drauf zugreife? Ich hätte getippt dass für wert ein neues objekt angelegt wird mit dem defaultkonstruktor, aber was wenn X keinen defaultkonstruktor hat?Anderes Problem, welche der Varianten ist jetzt eigentlich richtig und warum:
// Klasse blub wie oben // Variante 1 X weiter() { return blubobjekt.getWert(); } // Hier wird eine Kopie vom wert gemacht. Dann hat mir aber (so der compiler // nix optimiert) das ganze nix gebracht da ich ja am Ende doch eine Kopie mache? // Variante 2 X&& weiter() { return blubobjekt.getWert(); } // Wäre für mich am naheliegendsten. Allerdings sagt mir dann der // compiler "warning: returning reference to temporary" // und an einer stelle wo ich z.B. "cout << weiter();" mache die // Warnung "'<anonymous>' my be used uninitialized in this function". Da // kommt dann auch nix gescheites bei raus. Wieso? // Variante 3 X&& weiter() { return std::move(blubobjekt.getWert()); } // Nur noch die "anonymous"-Warnung aus Variante 2 // Variante 4 X&& weiter() { return std::forward<X>(blubobjekt.getWert()); } // Wie Variante 3. War forward nicht genau für so einen Fall gedacht?
Wie mach ichs jetzt richtig??
-
Hat vielleicht jemand einen Link wo ich weiter nachforschen kann, speziell zum zweiten Thema?
Oft wird ja http://cpp-next.com/archive/2009/09/move-it-with-rvalue-references/ empfohlen, aber da ist genau das nicht angesprochen..
-
> X&& getWert() { return wert; }
Gut auskennen tue ich mich noch nicht, aber hier willst du einen Lvalue (wert) an eine Rvalue-Referenz binden, und das verbietet dir dein Compiler logischerweise. Daher wirst du auch da um std::move nicht rumkommen.
-
Nein, nur weil du eine rvalue-Referenz zurückgibst, heißt das noch nicht, das deiner Variable der Wert geklaut wird. Die move Semantik kommt lediglich dadurch zustande, dass du nun Funktionen und Methoden für rvalue-Referenzen und lvalue-Referenzen unterschiedlich überladen kannst. Zum Beispiel Kopierkonstruktoren oder Zuweisungsoperatoren. Bekommen diese nun eine rvalue-Referenz, können sie davon ausgehen, dass das Objekt, auf das die Referenz zeigt, temporär ist und nichtmehr genutzt wird. Dann wird es eben ausgeschlachtet. Stell dir einen String vor, der intern einen Zeiger auf n Stück dynamischen Speicher hat. Statt nun diesen Speicher im Konstruktor des Strings von einem anderen String zu kopieren, können wir bei einem Konstruktor mit rvalue-Referenz auf nen anderen String einfach den Zeiger kopieren (und damit den Speicher klauen, aber egal der andere String wird ja nichtmehr genutzt). Die Funktion std::move macht in diesem Zusammenhang nur eins, sie castet deine übergebene Referenz nach rvalue-Referenz und erlaubt dir somit ein Objekt als temporär zu verkaufen.
-
mover schrieb:
Angenommen ich habe in etwa folgendes:
class blub { /*zeug*/ public: X&& getWert() { return wert; } private: X wert; };
Wird jetzt irgendwo "X ergebnis = blubobjekt.getWert();" mache dann geht der Wert ja zu "ergebnis" über und ist in blub::wert nicht mehr vorhanden, richtig?
Jain. Du musst hier schreiben
return std::move(wert);
. GCC akzeptiert das zwar noch so, ist aber nicht mehr erlaubt. Wenn X jetzt noch einen Move-Konstruktor hat, dann ja, der "Inhalt" wird inergebnis
landen undwert
wird verändert. Der Zustand vonwert
ist aber noch gültig. Beispiel: X=vector<int>. Dann wirdwert
einfach ein gültiger Vektor mit 0 Elementen sein. Der Move-Konstruktor verbiegt in diesem Beispiel ein paar Zeiger und "leert" den Quellvektor, statt Kopien zu erzeugen. Was da genau passiert ist also Sache des Move-Konstruktors.Was ist denn der Sinn dieser getWert-Funktion? Eine Rvalue-Referenz in diesem Fall zurückzugeben, ist eventuell nicht das richtige.
mover schrieb:
Anderes Problem, welche der Varianten ist jetzt eigentlich richtig und warum:
kommt drauf an, was Du machen willst
mover schrieb:
// Klasse blub wie oben // Variante 1 X weiter() { return blubobjekt.getWert(); } // Variante 2 X&& weiter() { return blubobjekt.getWert(); } // Wäre für mich am naheliegendsten. Allerdings sagt mir dann der // compiler "warning: returning reference to temporary" // Variante 3 X&& weiter() { return std::move(blubobjekt.getWert()); } // Nur noch die "anonymous"-Warnung aus Variante 2 // Variante 4 X&& weiter() { return std::forward<X>(blubobjekt.getWert()); } // Wie Variante 3. War forward nicht genau für so einen Fall gedacht?
Wie mach ichs jetzt richtig??
Variante 1 ist legal. Das Objekt, was die Funktion zurück gibt, wird vorzugsweise move-konstruiert, da getWert eine unbenannte Rvalue-Referenz liefert. Ist X also move-optimiert, wird hier keine Kopie erstellt.
Variante 2 ist auch legal. Die Compiler-Warnung ist Blödsinn. Hier wird die Rvalue-Referenz einfach weitergereicht. Allerdings muss man ja immer aufpassen, wenn man Referenzen zurückgibt, wie lang die noch gültig sind und so... Das gilt schon für Deine getWert-Funktion.
Variante 3 und 4 sind nur unnötig komplizierte Versionen von Variante 2. Ja,
forward
ist für das Weiterreichen gedacht, allerdings in die andere Richtung (Funktionsaufruf, nicht Rückgabe).kk
-
Ok, das hat jetzt gleich an ein paar stellen klick gemacht. Vielen Dank!
-
da http://www.c-plusplus.net/forum/viewtopic-var-t-is-271187-and-highlight-is-.html hat life soeben eine rvalue-Referenz sinnreich eingesetzt.