La semplice risposta è che dovresti scrivere il codice per i riferimenti ai valori come faresti con il codice dei riferimenti regolari e dovresti trattarli allo stesso modo il 99% delle volte. Ciò include tutte le vecchie regole sulla restituzione dei riferimenti (ovvero non restituire mai un riferimento a una variabile locale).
A meno che non si stia scrivendo una classe contenitore modello che deve sfruttare std :: forward ed essere in grado di scrivere una funzione generica che accetta riferimenti lvalue o rvalue, questo è più o meno vero.
Uno dei grandi vantaggi per il costruttore di spostamenti e l'assegnazione dei movimenti è che se li definisci, il compilatore può utilizzarli nel caso in cui RVO (ottimizzazione del valore di ritorno) e NRVO (denominata ottimizzazione del valore di ritorno) non vengano richiamati. Questo è abbastanza grande per restituire oggetti costosi come contenitori e stringhe in base al valore in modo efficiente dai metodi.
Ora dove le cose si fanno interessanti con i riferimenti rvalue, è che puoi anche usarli come argomenti per le normali funzioni. Ciò consente di scrivere contenitori con sovraccarichi sia per riferimento const (const foo e altro) sia per riferimento valore (foo && altro). Anche se l'argomento è troppo ingombrante per passare con una semplice chiamata del costruttore, può ancora essere fatto:
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));
}
I contenitori STL sono stati aggiornati per avere sovraccarichi di spostamento per quasi tutto (chiave e valori hash, inserimento vettoriale, ecc.), Ed è lì che li vedrai di più.
È inoltre possibile utilizzarli per le normali funzioni e, se si fornisce solo un argomento di riferimento al valore, è possibile forzare il chiamante a creare l'oggetto e lasciare che la funzione esegua lo spostamento. Questo è più un esempio che un ottimo uso, ma nella mia libreria di rendering ho assegnato una stringa a tutte le risorse caricate, in modo che sia più facile vedere cosa rappresenta ciascun oggetto nel debugger. L'interfaccia è qualcosa del genere:
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;
}
È una forma di "astrazione che perde" ma mi permette di sfruttare il fatto che ho dovuto creare la stringa già la maggior parte delle volte, evitando di farne ancora un'altra copia. Questo non è esattamente un codice ad alte prestazioni, ma è un buon esempio delle possibilità man mano che le persone ottengono il controllo di questa funzione. Questo codice richiede effettivamente che la variabile sia temporanea per la chiamata o invocato std :: move:
// move from temporary
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string("Checkerboard"));
o
// 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));
o
// 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));
ma questo non si compila!
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, str);