C++
Wertkategorien
Suche…
Bedeutungen der Wertkategorie
Ausdrücken in C ++ wird basierend auf dem Ergebnis dieser Ausdrücke eine bestimmte Wertkategorie zugewiesen. Wertkategorien für Ausdrücke können die Auflösung der C ++ - Funktion beeinflussen.
Wertkategorien bestimmen zwei wichtige, aber getrennte Eigenschaften eines Ausdrucks. Eine Eigenschaft ist, ob der Ausdruck eine Identität hat. Ein Ausdruck hat Identität, wenn er sich auf ein Objekt bezieht, das einen Variablennamen hat. Der Variablenname ist möglicherweise nicht in den Ausdruck einbezogen, das Objekt kann jedoch einen enthalten.
Die andere Eigenschaft ist, ob es legal ist, implizit vom Wert des Ausdrucks abzuweichen. Oder genauer gesagt, ob der Ausdruck, wenn er als Funktionsparameter verwendet wird, an R-Wert-Parametertypen bindet oder nicht.
C ++ definiert drei Wertkategorien, die die nützliche Kombination dieser Eigenschaften darstellen: lWert (Ausdrücke mit Identität, aber nicht aus, die verschoben werden können), xWert (Ausdrücke mit Identität, aus denen sich beweglich kann) und prvalue (Ausdrücke ohne Identität, aus denen sich verschieben lässt). C ++ hat keine Ausdrücke, die keine Identität haben und nicht verschoben werden können.
C ++ definiert zwei andere Wertkategorien, die jeweils ausschließlich auf einer dieser Eigenschaften basieren: glvalue (Ausdrücke mit Identität) und rvalue (Ausdrücke, aus denen verschoben werden kann). Diese dienen als nützliche Gruppierungen der vorherigen Kategorien.
Diese Grafik dient als Illustration:
prvalue
Ein prvalue-Ausdruck (pure-rvalue) ist ein Ausdruck, dem die Identität fehlt, dessen Auswertung normalerweise zur Initialisierung eines Objekts verwendet wird und aus dem implizit verschoben werden kann. Dazu gehören unter anderem:
- Ausdrücke, die temporäre Objekte darstellen, z. B.
std::string("123")
. - Ein Funktionsaufrufsausdruck, der keinen Verweis zurückgibt
- Ein Literal ( außer einem String-Literal - das sind lvalues), wie
1
,true
,0.5f
oder'a'
- Ein Lambda-Ausdruck
Der integrierte addressof-Operator ( &
) kann nicht auf diese Ausdrücke angewendet werden.
xvalue
Ein xvalue (eXpiring value) -Ausdruck ist ein Ausdruck, der eine Identität hat und ein Objekt darstellt, aus dem implizit verschoben werden kann. Die allgemeine Idee mit xvalue-Ausdrücken ist, dass das Objekt, das sie repräsentieren, bald zerstört werden wird (daher der "eXpiring" -Teil) und daher implizit ein Abgehen von ihnen in Ordnung ist.
Gegeben:
struct X { int n; };
extern X x;
4; // prvalue: does not have an identity
x; // lvalue
x.n; // lvalue
std::move(x); // xvalue
std::forward<X&>(x); // lvalue
X{4}; // prvalue: does not have an identity
X{4}.n; // xvalue: does have an identity and denotes resources
// that can be reused
lWert
Ein lvalue-Ausdruck ist ein Ausdruck, der eine Identität hat, jedoch nicht implizit verschoben werden kann. Dazu gehören Ausdrücke, die aus einem Variablennamen, Funktionsnamen, Ausdrücken, die integrierte Dereferenzierungsoperatoren sind, und Ausdrücken bestehen, die auf lvalue-Referenzen verweisen.
Der typische Wert ist einfach ein Name, aber Werte können auch in anderen Geschmacksrichtungen verwendet werden:
struct X { ... };
X x; // x is an lvalue
X* px = &x; // px is an lvalue
*px = X{}; // *px is also an lvalue, X{} is a prvalue
X* foo_ptr(); // foo_ptr() is a prvalue
X& foo_ref(); // foo_ref() is an lvalue
Während die meisten Literale (z. B. 4
, 'x'
usw.) Werte sind, sind String-Literale Werte.
glvalue
Ein glvalue-Ausdruck (ein "generalisierter lvalue") - Ausdruck ist jeder Ausdruck, der eine Identität hat, unabhängig davon, ob er verschoben werden kann oder nicht. Diese Kategorie umfasst l-Werte (Ausdrücke, die eine Identität haben, aus denen jedoch kein Platz verschoben werden kann) und x-Werte (Ausdrücke, die eine Identität haben und aus denen verschoben werden kann), schließt jedoch keine pr-Werte (Ausdrücke ohne Identität) aus.
Wenn ein Ausdruck einen Namen hat , ist dies ein guter Wert:
struct X { int n; };
X foo();
X x;
x; // has a name, so it's a glvalue
std::move(x); // has a name (we're moving from "x"), so it's a glvalue
// can be moved from, so it's an xvalue not an lvalue
foo(); // has no name, so is a prvalue, not a glvalue
X{}; // temporary has no name, so is a prvalue, not a glvalue
X{}.n; // HAS a name, so is a glvalue. can be moved from, so it's an xvalue
Wert
Ein rvalue-Ausdruck ist ein Ausdruck, aus dem implizit verschoben werden kann, unabhängig davon, ob er eine Identität hat.
Genauer gesagt, können rvalue-Ausdrücke als Argument für eine Funktion verwendet werden, die einen Parameter des Typs T &&
(wobei T
der Typ des expr
) verwendet. Nur Argumentausdrücke können als Argument für solche Funktionsparameter angegeben werden. Wenn ein nicht rvalue-Ausdruck verwendet wird, wählt die Überladungsauflösung eine Funktion aus, die keinen rvalue-Referenzparameter verwendet. Und wenn keine vorhanden sind, wird ein Fehler angezeigt.
Die Kategorie von rvalue-Ausdrücken umfasst alle xvalue- und prvalue-Ausdrücke und nur diese Ausdrücke.
Die Standardbibliotheksfunktion std::move
existiert, um einen nicht rvalue-Ausdruck explizit in einen rvalue zu transformieren. Insbesondere wird der Ausdruck in einen x-Wert umgewandelt, denn selbst wenn es sich um einen identitätslosen pr-Wert-Ausdruck handelt, erhält er durch Übergabe als Parameter an std::move
Identität (den Parameternamen der Funktion) und wird zum x-Wert.
Folgendes berücksichtigen:
std::string str("init"); //1
std::string test1(str); //2
std::string test2(std::move(str)); //3
str = std::string("new value"); //4
std::string &&str_ref = std::move(str); //5
std::string test3(str_ref); //6
std::string
hat einen Konstruktor, der einen einzelnen Parameter vom Typ std::string&&
, der üblicherweise als "Move-Konstruktor" bezeichnet wird. Die Wertkategorie des Ausdrucks str
ist jedoch kein r-Wert (insbesondere ein l-Wert), daher kann diese Konstruktorüberladung nicht aufgerufen werden. Stattdessen ruft sie den const std::string&
Konstruktor auf.
Zeile 3 verändert die Dinge. Der Rückgabewert von std::move
ist ein T&&
, wobei T
der Basistyp des übergebenen Parameters ist. Daher gibt std::move(str)
std::string&&
. Ein Funktionsaufruf, dessen Rückgabewert ein rvalue-Verweis ist, ist ein rvalue-Ausdruck (insbesondere ein xvalue). Daher kann er den Bewegungskonstruktor von std::string
aufrufen. Nach Zeile 3 wurde aus str
verschoben (dessen Inhalt ist jetzt undefiniert).
Zeile 4 übergibt ein temporäres std::string
an den Zuweisungsoperator von std::string
. Dies hat eine Überladung, die eine std::string&&
. Der Ausdruck std::string("new value")
ist ein rvalue-Ausdruck (insbesondere ein prvalue). Daher kann er diese Überladung aufrufen. Daher wird das temporäre str
in str
verschoben, und der undefinierte Inhalt wird durch bestimmte Inhalte ersetzt.
Zeile 5 erstellt eine benannte rvalue-Referenz mit dem Namen str_ref
, die auf str
verweist. Hier werden Wertkategorien verwirrend.
Während str_ref
ein rvalue-Verweis auf std::string
, ist die Wertkategorie des Ausdrucks str_ref
kein rvalue . Es ist ein lvalue Ausdruck. Ja wirklich. Aus diesem Grund kann der Bewegungskonstruktor von std::string
mit dem Ausdruck str_ref
. Zeile 6 kopiert daher den Wert von str
in test3
.
Um es zu verschieben, müssten wir erneut std::move
.