Die einfache Antwort ist, dass Sie Code für r-Wert-Referenzen schreiben sollten, wie Sie regulären Referenzcode verwenden würden, und dass Sie sie 99% der Zeit mental gleich behandeln sollten. Dies schließt alle alten Regeln zum Zurückgeben von Referenzen ein (dh niemals einen Verweis auf eine lokale Variable zurückgeben).
Sofern Sie keine Vorlagencontainerklasse schreiben, die std :: forward nutzen und eine generische Funktion schreiben muss, die entweder lvalue- oder rvalue-Referenzen verwendet, ist dies mehr oder weniger richtig.
Einer der großen Vorteile des Verschiebungskonstruktors und der Verschiebungszuweisung besteht darin, dass der Compiler sie verwenden kann, wenn Sie sie definieren, wenn die RVO (Rückgabewertoptimierung) und NRVO (benannte Rückgabewertoptimierung) nicht aufgerufen werden. Dies ist ziemlich umfangreich, um teure Objekte wie Container und Strings wertmäßig effizient aus Methoden zurückzugeben.
Wenn rvalue-Referenzen interessant werden, können Sie sie auch als Argumente für normale Funktionen verwenden. Auf diese Weise können Sie Container schreiben, die sowohl für die const-Referenz (const foo & other) als auch für die rvalue-Referenz (foo && other) überladen sind. Selbst wenn das Argument zu unhandlich ist, um mit einem bloßen Konstruktoraufruf übergeben zu werden, kann es dennoch getan werden:
std::vector vec;
for(int x=0; x<10; ++x)
{
// automatically uses rvalue reference constructor if available
// because MyCheapType is an unamed temporary variable
vec.push_back(MyCheapType(0.f));
}
std::vector vec;
for(int x=0; x<10; ++x)
{
MyExpensiveType temp(1.0, 3.0);
temp.initSomeOtherFields(malloc(5000));
// old way, passed via const reference, expensive copy
vec.push_back(temp);
// new way, passed via rvalue reference, cheap move
// just don't use temp again, not difficult in a loop like this though . . .
vec.push_back(std::move(temp));
}
Die STL-Container wurden aktualisiert, um Verschiebungsüberladungen für fast alles (Hash-Schlüssel und -Werte, Vektoreinfügung usw.) zu haben, und dort werden Sie sie am häufigsten sehen.
Sie können sie auch für normale Funktionen verwenden. Wenn Sie nur ein rvalue-Referenzargument angeben, können Sie den Aufrufer zwingen, das Objekt zu erstellen und die Funktion die Verschiebung ausführen zu lassen. Dies ist eher ein Beispiel als eine wirklich gute Verwendung, aber in meiner Rendering-Bibliothek habe ich allen geladenen Ressourcen eine Zeichenfolge zugewiesen, damit Sie leichter sehen können, was jedes Objekt im Debugger darstellt. Die Schnittstelle ist ungefähr so:
TextureHandle CreateTexture(int width, int height, ETextureFormat fmt, string&& friendlyName)
{
std::unique_ptr<TextureObject> tex = D3DCreateTexture(width, height, fmt);
tex->friendlyName = std::move(friendlyName);
return tex;
}
Es ist eine Form einer "undichten Abstraktion", aber ich kann die Tatsache ausnutzen, dass ich die Zeichenfolge bereits die meiste Zeit erstellen musste, und vermeiden, sie erneut zu kopieren. Dies ist nicht gerade ein Hochleistungscode, aber ein gutes Beispiel für die Möglichkeiten, wenn die Leute den Dreh raus haben. Dieser Code erfordert, dass die Variable entweder temporär für den Aufruf ist oder std :: move aufgerufen wird:
// move from temporary
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string("Checkerboard"));
oder
// explicit move (not going to use the variable 'str' after the create call)
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, std::move(str));
oder
// explicitly make a copy and pass the temporary of the copy down
// since we need to use str again for some reason
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string(str));
aber das wird nicht kompiliert!
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, str);