K
booster schrieb:
Möchte ich einen string in eine klasse (Konstruktor) übergeben habe ich das bisher immer per Referenz getan:
MyClass(const std::string& mystring)
: _mystring(mystring)
Ja, so hat man das früher gemacht.
booster schrieb:
Seit c++ 11 hat man die Möglichkeit das per Move semantik und r-value referenz zu tun.
MyClass(std::string&& mystring)
: _mystring(std::move(mystring))
Das ist zwar so möglich, aber unpraktisch, weil dieser Konstruktor nicht mehr alles akzeptiert, nur noch Rvalue-Argumente:
string x = "hello";
MyClass o {x}; // Klappt nicht mehr, da x ein Lvalue ist.
Du könntest jetzt den Konstruktor überladen:
MyClass(string const& s) : _mystring(s) {}
MyClass(string && s) : _mystring(move(s)) {}
So funktioniert das wieder wie erwartet. Aber sowas ufert schnell mal aus. Diese Art der Überladung explodiert mit der Zahl der Parameter.
Der nächste Schwierigkeitsgrad, der die Überladungsexplosion umgeht wäre "perfect forwarding". Aber gerade bei Konstruktoren ist das nicht unproblematisch, weil so ein generischer Forwarding Konstruktor dann auch schnell mal für ein Copy oder Move-Konstruktor gehalten werden kann (bzgl Überladungsauflösung). Deswegen zweige ich das erst gar nicht.
Warum nicht auch einfach, wenn es einfach geht? ...
booster schrieb:
Was mich nun wundert:
Ich nutze den Resharper für c++ und der schlägt mir für das erste Beispiel vor:
"Pass by value and use std::move [modernize-pass-by-value]"
und macht dann das hier draus:
MyClass(std::string mystring)
: _mystring(std::move(mystring))
Ja, so würde ich das auch schreiben. Im Vergleich zu der Überladung zwischen const-Referenz und rvalue-Referenz von oben ist diese Lösung natürlich kürzer und etwas leserlicher. Sie kostet ggf nur einen extra "Move". Aber von string wissen wir, dass der Move-Konstruktor sehr effizient ist. Wie das mystring-Objekt konstruiert wird, wird hier dem Compiler überlassen. Das kann, muss aber nicht eine Kopie sein. Im besten Fall wird das alles wegoptimiert ("copy/move elision"). Wenn Du schreibst
string getstring()
MyClass o {getstring()};
wird bei der pass-by-value Lösung heutzutage die getstring-Funktion das String-Objekt direkt dort konstruieren, wo es später einfach zu mystring umbenannt wird. Und dann findet ein Move von mystring zu _mystring statt, der relativ billig ist.
Ich finde, dass wir uns gerade bei move-optimierten Typen zu mehr pass-by-value trauen sollten. In diesem Fall sind wir an einer eigenen string-"Kopie" interessiert. Warum als nicht pass-by-value? Lass Den Compiler das Problem lösen, wie dieser Parameter-String zu Stande kommt. Da kann viel optimiert werden.
In anderen Fällen, nämlich wo man den String nur ggf untersuchen will und keine eigene Kopie braucht, könnte heute string_view interessant sein. Perfekte ist kein Ansatz. Es sind immer Abwägungen. Auch string_view hat seine Schwäche: Man bekommt von einem string_view keinen const char* mehr, der auf eine nullterminierte Zeichenkette zeigt.