In C++ lvalue (left value) è un'espressione che si riferisce a un oggetto in memoria, che ha un nome e a cui si può fare riferimento (ad esempio, una variabile). rvalue (right value) è un valore temporaneo, privo di nome, che non è un oggetto nel senso tradizionale (ad esempio, il risultato di a + b, literali come 5).
Si può prendere l'indirizzo di un lvalue, ma non di un rvalue (fino all'introduzione delle rvalue references). Per passare questi valori alle funzioni ci sono:
void foo(const std::string& s); — accettano lvalue e rvalue.void bar(std::string& s); — accettano solo lvalue.void baz(std::string&& s); — accettano solo rvalue.Esempio:
void takeValue(std::string& s) { } // lvalue void takeRValue(std::string&& s) { } // rvalue std::string s = "hello"; takeValue(s); // OK, lvalue takeRValue(std::string("hi")); // OK, rvalue
Le rvalue references sono necessarie per il passaggio efficiente di oggetti temporanei, principalmente per la move semantics, per spostare risorse invece di copiarle.
Quale tipo di riferimento (lvalue o rvalue) riceverà l'espressione
std::move(obj)? Quale categoria diventerà l'oggetto stesso dopo l'applicazione di std::move?
Risposta:
std::move(obj) restituisce sempre un riferimento a rvalue (T&&), ma l'oggetto stesso rimane un lvalue, è semplicemente applicata una conversione esplicita. Dopo questo, si deve trattare l'oggetto con grande cautela (può essere in uno stato non definito, ma valido).
Esempio:
std::string s = "data"; std::string d = std::move(s); // d riceve i dati da s, s ora è vuota
Storia
In un grande progetto, uno degli sviluppatori passava oggetti temporanei tramite riferimento a lvalue T& (invece di T&& o const T&). Questo portava a errori di compilazione e copie inefficaci — la move semantics non veniva usata, la performance diminuiva del 40%.
Storia
Nel motore frontend, veniva applicato in modo errato std::move a variabili che venivano dopo utilizzate nuovamente. Di conseguenza, le variabili erano in uno stato "danneggiato", si verificavano crash e si interrompevano i thread di rendering.
Storia
Nella libreria di serializzazione veniva passato un contenitore di tipo std::vector<T> a una funzione come lvalue, mentre si aspettavano move. Invece di spostare, si effettuava una costosa copia di un gran numero di elementi, il che peggiorava drasticamente i tempi di serializzazione su grandi array.