Copy Elision und Co.
-
Nachdem ich in einem Thread diesem Link gefolgt bin und mir den Text durchgelesen habe, bin ich dann doch ein wenig verwirrt.
Vor ein paar Jahren hieß es noch:
Übergib große Daten als const ref.void foo(const std::string &string);Entsprechend habe ich das in meinem Projekt so durch gezogen. Jetzt soll das auf einmal böse sein? Meinem Verständnis nach wird hier ebenso eine Kopie avoided. Warum soll das jetzt schlechter sein?
Ebenso folgendes:
std::string& foo(const std::string &string) { //... return string; }Das ist ja jetzt angeblich auch böse? o.O
Statt dessen soll ich jetzt folgendes schreiben:std::string foo(std::string string) { //... std::string newString; std::swap(newString, string); return newString; }Was hier dran der Vorteil sein soll, versteh ich nicht.
In irgendeinem Thread habe ich gelesen, das man große Daten mit
return std::move(foo);zurück geben soll. Welchen Unterschied macht das zu der in dem Text gepredigten Variante?
mfg
-
return std::move(foo);Ist meint Blödsinn. Es erzwingt ein Move oder Copy an einer Stelle wo meist die ganze Kopie wegoptimiert werden kann.
-
anti-freak schrieb:
Vor ein paar Jahren hieß es noch:
Übergib große Daten als const ref.void foo(const std::string &string);Entsprechend habe ich das in meinem Projekt so durch gezogen. Jetzt soll das auf einmal böse sein? Meinem Verständnis nach wird hier ebenso eine Kopie avoided. Warum soll das jetzt schlechter sein?
Ich glaube das hast du falsch verstanden. Wenn du innerhalb der Funktion foo den String nicht kopieren möchtest, nimmt du weiterhin eine const Referenz. Wenn du aber ohnehin eine Kopie benötigst ist es sinnvoll, diese direkt beim Aufruf zu machen, da dann ggf. gemoved werden kann oder sogar die Kopie komplett wegoptimiert werden kann.
-
anti-freak schrieb:
Ebenso folgendes:
std::string& foo(const std::string &string) { //... return string; }Das ist ja jetzt angeblich auch böse? o.O
Statt dessen soll ich jetzt folgendes schreiben:std::string foo(std::string string) { //... std::string newString; std::swap(newString, string); return newString; }Was hier dran der Vorteil sein soll, versteh ich nicht.
Das Problem ist, das erste Funktion immer eine Kopie machen muss, da innerhalb der Funktion nicht mehr feststellbar ist, ob das Argument nach dem Aufruf noch benötigt wird.
-
Man muss bei der Übergabe zwischen zwei Dingen unterscheiden:
-Wird Besitz an die Funktion übergeben? D.h, dass Argument innerhalb der Funktion kopiert (z.B. in eine Membervariable)
-Wird das Argument einfach nur verwendet und nicht verändert?Im letzteren Fall ist eine const Referenz das optimale, aber der Erste Fall hat einige Probleme.
Übergibt man dort wie in C++03 eine const Referenz ist das optimal, wenn man LValues übergibt. Dort wird dann von dem const Referenz zum Ziel kopiert. Übergibt man jedoch ein Temporary (also ein RValue), wird erst eine Kopie erstellt, dann die const Referenz auf die Kopie übergeben und anschließend erneut kopiert. Da Kopien idR teuer sind, könnte das zu Problemen führen.
Deswegen ist es optimal wenn man jede Funktion mit einer const LValue Referenz und einer RValue Referenz überlädt. Der erste Fall ist gut für LValues, der zweite Fall gut für RValues, da dort direkt ins Ziel gemoved werden kann, was billiger als eine Kopie ist.
Da diese Überladungen bei N Argumenten zu 2^N Funktionen führen kann, nimmt man quasi als Kompromiss pass-by-Value. Bei LValues wird ins Argument kopiert und dann kann man vom Argument ins Ziel moven (explizit mit std::move), bei RValues wird ins Argument gemoved und man kann dan wie bei LValues ins Ziel moven.return std::move(foo), dient nur dazu da, wenn man etwas aus der Funktion raus moven muss, was nicht automatisch geschieht, eine in der Funktion lokale Variable wird automatisch aus der Funktion gemoved.
-
anti-freak schrieb:
Das ist ja jetzt angeblich auch böse? o.O
Statt dessen soll ich jetzt folgendes schreiben:std::string foo(std::string string) { //... std::string newString; std::swap(newString, string); return newString; }Ist auch Blödsinn wenn ich das richtig sehe. Vielleicht meist du etwas wie
T& operator=(T x) // x is a copy of the source; hard work already done { swap(*this, x); // trade our resources for x's return *this; // our (old) resources get destroyed with x }um mal den Text zu zitieren. Das ist durchaus sinvoll.
-
TNA schrieb:
anti-freak schrieb:
Das ist ja jetzt angeblich auch böse? o.O
Statt dessen soll ich jetzt folgendes schreiben:std::string foo(std::string string) { //... std::string newString; std::swap(newString, string); return newString; }Ist auch Blödsinn wenn ich das richtig sehe. Vielleicht meist du etwas wie
Das soll wohl RVO ermöglichen, das nicht mit einem Übergabeparameter funktioniert. Ob das so aber in C++1 mit move semantics noch einen Vorteil hat?
-
Danke, auch wenn ich die Posting-Methode von "TNA" etwas seltsam finde, habt ihr mir weiter geholfen.

mfg
-
anti-freak schrieb:
Danke, auch wenn ich die Posting-Methode von "TNA" etwas seltsam finde, habt ihr mir weiter geholfen.

mfg
Ja sorry, ich sollte besser erst komplett lesen und zu ende denken bevor ich etwas poste, aber manchmal, insbesondere bei spannenden Themen kann ich mich bei antworten nur schwer bis gar nicht bremsen.
-
Der Artikel (besonders dessen Titel) mag leicht verwirrend sein. Pass By Value ist nur von Vorteil, wenn der Besitz übertragen wird. D.h. wenn du den String sowieso umkehren musst und das in-place machst, statt eine potentiell unnötige Kopie zu erstellen. Wenn dir in der Funktion eine Referenz reicht, weil du nur lesen musst, dann nimm diese weiterhin.
TNA, es gibt eine Editierfunktion
