Quote:
Originally Posted by Padmak
Kannst du kurz ein Beispiel zeigen, was du mit "überladen für rvalue Referenzen" in diesem Fall genau meinst?
|
Sagen wir, wir haben so eine Funktion:
Code:
void myFunc(const std::string& arg){
val = arg;
}
Damit haben wird ja einmal arg kopiert.
Wenn wir jetzt noch eine 2. Funktion haben:
Code:
std::string getString(){ /*von irgendwo wird hier ein string zurück gegeben*/}
Und oft die 1. Funktion so aufrufen:
Code:
myFunc(getString());
Dann ist der typ von dem Parameter eigentlich std::string&&. Da wir die Funktion aber nur ein const std::string& annehmen lassen, wird diese Variante aufgerufen und wir bekommen keinen Vorteil dadurch, dass der Parameter eine rvalue Referenz eigentlich ist. Wenn wir die Funktion jetzt so überladen:
Code:
void myFunc(std::string&& arg)
{
var = std::move(arg);
}
Nutzen wir halt diesen Fall aus und kopieren den String nicht. Und wenn es häufig vorkommt, dass man rvalue Referenzen übergibt lohnt es sich schon die Funktion für diese zu überladen. Noch ein Beispiel bei dem eine rvalue Referenzen übergeben wird, was häufig vorkommen kann:
Code:
someFunc(arg1 + arg2); // 1.
Wenn jetzt arg1 und arg2 nicht grade integer (oder Zahlen an sich sind) könnte das kopieren dann doch schon langsamer sein, als std::move zu nutzen, z.B. wenn arg1 und arg2 strings sind.
Quote:
Originally Posted by Padmak
Edit:
Vor allem bei solchen Sachen wie Strings ist es mir schon länger relativ egal, ob die jetzt ein mal mehr oder weniger kopiert werden - Die meisten meiner Programme haben immer relativ deutliche Bottlenecks, so wie IO oder sonstige Benutzereingaben, da ist es bisher noch nie an zu vielen temporären Kopien von irgendwas gescheitert ;)
|
Ja bei den meisten Strings ist das nicht wichtig. Aber es kann halt sein, dass man von irgendeinem Datei Format lange Zeilen einliest und dann ungefähr sowas hat:
Code:
myFunc(readNextLine());
Und wenn das wirklich lange Zeilen sind, dann kann man das schon merken (vor allem, wenn das in ner Loop aufgerufen wird). Edit: Wobei ich hier wohl auch sagen muss, dass man readNextLine besser so deklarieren sollte:
Code:
void readNextLine(std::string& out);
Code:
std::string l;
while(true)
readNextLine(l);
Wenn die 1. Zeile 1000 Zeichen hat und die 2. 500, muss da nichts mehr allokiert werden. Bei der ersten Variante wird da für jede Zeile Speicher allokiert.
Aber für kleine Strings hast du definitiv recht. Bei weniger als 12 Zeichen pro String kann ein move sogar
länger als ein copy dauern, da bei so kurzen strings etwas namens small string optimization angewandt wird, bei dem kein Speicher für den string allokiert wird, sondern interne Variablen vom std::string dafür genutzt werden. std::string hat normal 3 Variablen: die Länge, die Größe des allokierten Speichers und einen pointer auf diesen Speicher. Bei 32 bit sind das 12 bytes. Wenn man jetzt nur 11 Zeichen hat, dann darf die standard library diese 11 Zeichen direkt in den Variablen speichern. Das heißt, dass das 1. Byte der Länge dann wirklich die Länge speichert, die letzten 3 aber die 1. 3 Zeichen des strings. Auch in die anderen 2 Variablen darf die Klasse dann jeweils 4 Zeichen speichern, womit man dann bei insgesamt 11 Zeichen wäre, ohne extra Speicher für diese zu allokieren. Dann kann ein move von diesem String langsamer als ein copy sein. Ein "normaler" move von einem String ist generell schneller, weil da kein Speicher allokiert werden muss (und der string sich den pointer einfach klaut). Aber wenn sowieso kein Speicher allokiert werden muss, dann sind sowohl ein copy, als auch ein move, 3mal das kopieren von 4 bytes. Wobei es beim move noch sein kann, dass es die Variablen von dem String, von dem Sachen genommen werden, auf einen default Wert setzen muss. Dabei bin ich mir aber nicht sicher und ich weiß nur, dass das Objekt, von dem gemovt wird, in einem "valid state" sein muss (was es ja wäre, selbst wenn nichts auf einen default Wert gesetzt wird. Deswegen ist es unwahrscheinlich, dass es wirklich langsamer ist, aber eine Implementation dürfte es langsamer machen).
tl;dr: Bei < 11 Zeichen KÖNNTE (Implementations abhängig und unwahrscheinlich) es sein, dass ein move länger dauert als ein copy für einen std::string. Also hast du schon recht, dass man mit dem Optimieren nicht übertreiben sollte, aber manchmal kann etwas so triviales wie "Lieber std::string returnen oder über Referenz nach außen übergeben?" schon einen großen Unterschied machen.
Nebenbei: QString und std::string sind afaik schon sehr underschiedlich und nichts, was ich hier über std::string sage muss auf QString zutreffen.