Werte-Kategorien und Rvalue-Referenzen in C++

In C++ ist ein Ausdruck durch einen Datentyp und die Werte-Kategorien Lvalue und Rvalue charakterisiert. Ein Lvalue hat typischerweise einen Namen und eine Speicheradresse und kann über diese Identität auch außerhalb des aktuellen Ausdrucks verwendet werden. Ein Rvalue hingegen hat keine Identität, da er ursprünglich nur für eine temporäre Verwendung vorgesehen war. Bis C++11 war es somit nicht möglich einen Rvalue und dessen Speicherbereich optimal nutzen zu können 1).

 value-categories

Seit C++11 werden Glvalue, Xvalue und Prvalue als zusätzliche Werte-Kategorien verwendet, um Spracherweiterungen zur optimierten Verwendung von Rvalues berücksichtigen zu können. Rvalues sind in die beiden Werte-Kategorien Xvalue und Prvalue unterteilt worden, wobei Prvalue im Gegensatz zu Xvalues Ausdrücke ohne Identität darstellen. Der Prvalue kann somit auch als reiner Rvalue gesehen werden. Im Weiteren sind Lvalue und Xvalue zur Werte-Kategorie Glvalue zusammengefasst worden, da hier Ausdrücke beider Werte-Kategorien eine Identität aufweißen.

Die eigentliche Neuerung und somit Grund für die Anpassung der Werte-Kategorien umfasst aber vor allem die Xvalues, bzw. die nun mögliche weitere Verwendung der Ressourcen von Rvalues. Ohne eine Berücksichtigung des Sprachumfangs von C++ werden diese Ressourcen sonst ungenutzt verworfenen. Auf die hierbei wichtige Verwendung der Rvalue-Referenzen wird weiter unten noch näher eingegangen.

Beispiel Beschreibung
int x, y Variablen sind Lvalues
int *p = &x Pointer sind Lvalues
string a, b String Variablen sind Lvalues
((x < 3) ? x : y) = 2 Ergebnisse von Fallunterscheidungen sind Lvalues
a + b, x * 3 Ergebnisse von Ausdrücken sind temporäre Objekte ohne Identität und damit Prvalues
„prvalue“ Strings ohne Identität sind Prvalues
3, null, true Literale sind Prvalues

Der Rückgabewert einer Funktion ist ein Prvalue.

int rvalue() {return 1;}
rvalue();

Der Rückgabewert einer Funktion mit einer Referenz auf einen Lvalue ist ein Lvalue.

int value = 2;
int& lvalue() {return value;}

int main()
{
  lvalue() = 1; // Lvalue = Rvalue  
  return 0;
}

Der Rückgabewert einer Funktion mit einer Referenz auf einen RValue ist ein Xvalue.

int&& rvalue() {return 3;}
rvalue(); // Xvalue

Der Rückgabewert eines Objekts und seiner Membervariablen mit einer Referenz auf einen RValue ist ein Xvalue.

struct A { int m = 4; };
A&& a() { return A(); };

int main()
{
  a(); // Xvalue
  a().m; // Xvalue
  return 0;
}

Rvalue-Referenzen werden verwendet, um die eigentlich unnötigen Kopien bei der Zuweisung von Variablen zu vermeiden und damit den Speicher optimal nutzen zu können. So wird bei einer einfachen Addition der Form c = a + b der Rvalue a + b als temporärer Wert, dem Xvalue, im Speicher abgelegt bevor er in den Lvalue c kopiert und dann auch wieder aus dem Speicher entfernt wird.

Der Speicher für diesen Wert, sowie die notwendige Kopier- und Löschoperation lassen sich seit C++11 unter Verwendung der Rvale-Referenzen vermeiden, indem der Rvalue noch vor einer Kopie dem Lvalue zugewiesen wird.

Eine RValue-Referenz auf den Rvalue einer Additionsoperation verhindert dessen Kopie.

int&& c = a + b;

Eine RValue-Referenz auf den Parameter einer Funktion verhindert dessen Kopie.

int rvalue(int&& rvalue) {
   return rvalue + 1;
}

Ähnlich zu den LValue-Referenzen werden RValue-Referenzen bei Variablen und Funktionen mit dem Operator && gekennzeichnet und können auch als Alias auf den temporären Speicherbereich des Rvalue bezeichnet werden.

<< Neuere Einträge | Ältere Einträge >>