In C++ is lvalue (left value) een expressie die verwijst naar een object in het geheugen, dat een naam heeft en waarnaar kan worden verwezen (bijvoorbeeld een variabele). rvalue (right value) is een tijdelijke waarde, zonder naam, en is geen object in de traditionele zin (bijvoorbeeld het resultaat van a + b, literalen zoals 5).
Lvalue kan worden gepakt als adres, maar rvalue kan dat niet (tot de introductie van rvalue-referenties). Voor het doorgeven ervan aan functies bestaan:
void foo(const std::string& s); — accepteren lvalue en rvalue.void bar(std::string& s); — accepteren alleen lvalue.void baz(std::string&& s); — accepteren alleen rvalue.Voorbeeld:
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
Rvalue-referenties zijn nodig voor een effectieve overdracht van tijdelijke objecten, voornamelijk voor move-semantiek, om middelen te verplaatsen in plaats van ze te kopiëren.
Welke soort referentie (lvalue of rvalue) zal de expressie
std::move(obj)verkrijgen? Tot welke categorie zal het object zelf behoren na het toepassen van std::move?
Antwoord:
std::move(obj) geeft altijd een rvalue-referentie (T&&) terug, maar het object zelf blijft een lvalue, gewoonweg toegepaste expliciete conversie. Hierna moet je uiterst voorzichtig met het object omgaan (het kan in een onbepaalde, maar geldige toestand zijn).
Voorbeeld:
std::string s = "data"; std::string d = std::move(s); // d krijgt de data van s, s is nu leeg
Verhaal
In een groot project gaf een van de ontwikkelaars tijdelijke objecten door via een lvalue-referentie T& (in plaats van T&& of const T&). Dit leidde tot compilatiefouten en suboptimale kopieën — move-semantiek werd niet gebruikt, wat leidde tot een prestatievermindering van 40%.
Verhaal
In de frontend-engine werd std::move verkeerd toegepast op variabelen die daarna opnieuw werden gebruikt. Dit resulteerde in "vernietigde" variabelen, leidend tot crashende applicaties en renders.
Verhaal
In de serialisatiebibliotheek werden containers van het type std::vector<T> als lvalue naar een functie doorgegeven, terwijl een move werd verwacht. In plaats van te verplaatsen, werden dure kopieën gemaakt van een groot aantal elementen, wat de serialisatietijd voor grote arrays aanzienlijk verslechterde.