In C++ ist ein lvalue (left value) ein Ausdruck, der sich auf ein Objekt im Speicher bezieht, das einen Namen hat und auf das verwiesen werden kann (z. B. eine Variable). Ein rvalue (right value) ist ein temporärer Wert, der keinen Namen hat und nicht im traditionellen Sinne ein Objekt ist (z. B. das Ergebnis von a + b, Literale wie 5).
Ein lvalue kann adressiert werden, während ein rvalue nicht adressiert werden kann (bis zur Einführung von rvalue-Referenzen). Um sie an Funktionen zu übergeben, gibt es:
void foo(const std::string& s); — akzeptiert lvalue und rvalue.void bar(std::string& s); — akzeptiert nur lvalue.void baz(std::string&& s); — akzeptiert nur rvalue.Beispiel:
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-Referenzen sind notwendig für die effiziente Übergabe von temporären Objekten, hauptsächlich für die Move-Semantik, um Ressourcen zu verschieben und nicht zu kopieren.
Welchen Referenztyp (lvalue oder rvalue) erhält der Ausdruck
std::move(obj)? Welche Kategorie hat das Objekt nach der Anwendung von std::move?
Antwort:
std::move(obj) gibt immer eine rvalue-Referenz (T&&) zurück, aber das Objekt bleibt ein lvalue; es wird lediglich eine explizite Umwandlung angewendet. Nach dieser Umwandlung muss vorsichtig mit dem Objekt umgegangen werden (es kann sich in einem undefinierten, aber gültigen Zustand befinden).
Beispiel:
std::string s = "data"; std::string d = std::move(s); // d erhält die Daten von s, s ist jetzt leer
Geschichte
In einem großen Projekt übergab einer der Entwickler temporäre Objekte über eine lvalue-Referenz T& (statt T&& oder const T&). Dies führte zu Kompilierungsfehlern und suboptimalen Kopien — die Move-Semantik wurde nicht genutzt, die Leistung verringerte sich um 40%.
Geschichte
Im Frontend-Engine wurde std::move fälschlicherweise auf Variablen angewandt, die danach wieder verwendet wurden. Dadurch waren die Variablen in einem "zerstörten" Zustand, es kam zu Abstürzen und Renderer-Threads fielen aus.
Geschichte
In der Serialisierungsbibliothek wurde ein Container des Typs std::vector<T> als lvalue an eine Funktion übergeben, während ein Move erwartet wurde. Statt einer Verschiebung fand eine teure Kopie vieler Elemente statt, was die Serialisierungszeit bei großen Arrays stark verschlechterte.