Wertekategorien und zugehörige VerweiseValue categories, and references to them

In diesem Thema werden die verschiedenen Kategorien von Werten (und Verweisen auf Werte) beschrieben, die in C++ vorhanden sind.This topic describes the various categories of values (and references to values) that exist in C++. Sicherlich haben Sie bereits von lvalues und rvalues gehört, der in diesem Artikel erläuterte Zusammenhang ist Ihnen jedoch möglicherweise neu.You will doubtless have heard of lvalues and rvalues, but you may not think of them in the terms that this topic presents. Außerdem gibt es noch andere Arten von Werten.And there are other kinds of values, too.

Jeder Ausdruck in C++ liefert einen Wert, der einer der in diesem Thema erläuterten Kategorien angehört.Every expression in C++ yields a value that belongs to one of the categories discussed in this topic. Es gibt Aspekte der C++-Sprache, ihrer Merkmale und Regeln, die ein umfassendes Verständnis dieser Wertekategorien sowie der Verweise auf sie voraussetzen.There are aspects of the C++ language, its facilies, and rules, that demand a proper understanding of these value categories, and references to them. Beispiele hierfür sind das Übernehmen der Adresse eines Werts, das Kopieren eines Werts, das Verschieben eines Werts sowie das Weiterleiten eines Werts an eine andere Funktion.For example, taking the address of a value, copying a value, moving a value, and forwarding a value on to another function. In diesem Thema werden nicht alle diese Aspekte ausführlich erörtert, Sie erhalten jedoch grundlegende Informationen, die ausreichend für ein allgemeines Verständnis sind.This topic doesn't go into all of those aspects in depth, but it provides foundational information for a solid understanding of them.

Die Informationen in diesem Thema beziehen sich auf die Stroustrup-Analyse von Wertekategorien anhand der zwei unabhängigen Eigenschaften der Identität und Verschiebbarkeit [Stroustrup, 2013].The info in this topic is framed in terms of Stroustrup's analysis of value categories by the two independent properties of identity and movability [Stroustrup, 2013].

Ein lvalue weist eine Identität auf.An lvalue has identity

Was bedeutet es für einen Wert, eine Identität zu besitzen?What does it mean for a value to have identity? Wenn Sie über die Speicheradresse eines Werts verfügen (oder übernehmen können) und diese sicher verwenden, weist der Wert eine Identität auf.If you have (or you can take) the memory address of a value and use it safely, then the value has identity. Auf diese Weise können Sie viel mehr tun, als lediglich den Inhalt von Werten vergleichen: Sie können sie anhand der Identität vergleichen und voneinander unterscheiden.That way, you can do more than compare the contents of values: you can compare or distinguish them by identity.

Ein lvalue weist eine Identität auf.An lvalue has identity. Heute ist es nur ein „historischer“ Fakt, dass das „l“ in „lvalue“ als Abkürzung für „links“ steht (das heißt, die linke Seite einer Zuweisung).It's now a matter of only historical interest that the "l" in "lvalue" is an abbreviation of "left" (as in, the left-hand-side of an assignment). In C++ kann ein lvalue auf der linken oder rechten Seite einer Zuweisung stehen.In C++, an lvalue can appear on the left or on the right of an assignment. Daher kann vom „l“ in „lvalues“ nicht wirklich abgeleitet werden, worum es sich bei diesen Werten handelt.The "l" in "lvalues", then, doesn't actually help you to comprehend nor define what they are. Sie müssen lediglich wissen, dass ein lvalue ein Wert ist, der eine Identität besitzt.You need only to understand that what we call an lvalue is a value that has identity.

Beispiele für Ausdrücke, die lvalues darstellen: eine benannte Variable bzw. Konstante und eine Funktion, die einen Verweis zurückgibt.Examples of expressions that are lvalues include: a named variable or constant; or a function that returns a reference. Beispiele für Ausdrücke, die keine lvalues sind: ein temporärer Wert oder eine Funktion, deren Rückgabe nach Wert erfolgt.Examples of expressions that are not lvalues include: a temporary; or a function that returns by value.

int& get_by_ref() { ... }
int get_by_val() { ... }

int main()
{
    std::vector<byte> vec{ 99, 98, 97 };
    std::vector<byte>* addr1{ &vec }; // ok: vec is an lvalue.
    int* addr2{ &get_by_ref() }; // ok: get_by_ref() is an lvalue.

    int* addr3{ &(get_by_ref() + 1) }; // Error: get_by_ref() + 1 is not an lvalue.
    int* addr4{ &get_by_val() }; // Error: get_by_val() is not an lvalue.
}

lvalues weisen eine Identität auf, und dasselbe gilt für xvalues.Now, while it's a true statement that lvalues have identity, so do xvalues. Auf das Wesen eines xvalue wird später in diesem Artikel eingegangen.We'll go more into what an xvalue is later in this topic. Vorerst soll nur kurz darauf hingewiesen werden, dass es eine Wertekategorie namens „glvalue“ gibt (dies steht für „generalisierter lvalue“).For now, just be aware that there is a value category called glvalue, for "generalized lvalue". Die Obermenge von glvalues enthält sowohl lvalues (die auch als klassische lvalues bezeichnet werden) als auch xvalues.The superset of glvalues contains both lvalues (also known as classical lvalues) and xvalues. Die Aussage„ein lvalue weist eine Identität auf“ trifft zwar zu, aber die Gesamtheit der Elemente mit einer Identität ist die Menge der glvalues, wie in dieser Abbildung veranschaulicht.So, while "an lvalue has identity" is true, the complete set of things that have identity is the set of glvalues, as shown in this illustration.

Ein lvalue weist eine Identität auf.

Ein rvalue ist verschiebbar, während dies für einen lvalue nicht zutrifft.An rvalue is movable; an lvalue is not

Es gibt jedoch auch Werte, die keine glvalues sind.But there are values that are not glvalues. Demzufolge gibt es Werte, für die Sie nicht die Speicheradresse abrufen können (bzw. bei denen Sie sich nicht darauf verlassen können, dass sie gültig ist).Consequently, there are values that you can't obtain a memory address for (or you can't rely on it to be valid). Einige dieser Werte haben wir im obigen Codebeispiel gesehen.We saw some such values in the code example above. Das klingt wie ein Nachteil.This sounds like a disadvantage. Tatsächlich ist ein Wert wie dieser von Vorteil, weil Sie daraus verschieben können (was generell kostengünstig ist), und nicht daraus kopieren müssen (was generell teuer ist).But in fact the advantage of a value like that is that you can move from it (which is generally cheap), rather than copy from it (which is generally expensive). Verschieben aus einem Wert bedeutet, dass er sich nicht mehr an der ursprünglichen Stelle befindet.Moving from a value means that it's no longer in the place it used to be. Der Versuch, auf den Wert an seiner ursprünglichen Stelle zuzugreifen, sollte daher vermieden werden.So, trying to access it in the place it used to be is something to be avoided. Eine Erörterung, unter welchen Umständen und wie ein Wert verschoben wird, sprengt den Rahmen dieses Artikels.A discussion of when and how to move a value is out of scope for this topic. Für diesen Artikel müssen wir lediglich wissen, dass ein verschiebbarer Wert als rvalue (oder klassischer rvalue) bezeichnet wird.For this topic, we just need to know that a value that is movable is known as an rvalue (or classical rvalue).

Das „r“ in „rvalue“ steht für „rechts“ (d. h. die rechte Seite einer Zuweisung).The "r" in "rvalue" is an abbreviation of "right" (as in, the right-hand-side of an assignment). Sie können jedoch rvalues und Verweise auf rvalues außerhalb von Zuweisungen verwenden.But you can use rvalues, and references to rvalues, outside of assignments. Wir konzentrieren uns also nicht auf das „r“ in „rvalues“.The "r" in "rvalues", then, is not the thing to focus on. Sie müssen lediglich wissen, dass ein rvalue ein Wert ist, der verschoben werden kann.You need only to understand that what we call an rvalue is a value that is movable.

Ein lvalue hingegen kann nicht verschoben werden, wie in dieser Abbildung veranschaulicht.An lvalue, conversely, isn't movable, as shown in this illustration. Ein verschobener lvalue würde der Definition eines lvalue widersprechen und ein unerwartetes Problem für Code bewirken, in dem sinnvollerweise erwartet wird, dass weiterhin auf den lvalue zugegriffen werden kann.An lvalue that moved would defy the definition of lvalue, and it would be an unexpected problem for code that very reasonably expected to be able to continue to access the lvalue.

Ein rvalue ist verschiebbar, während dies für einen lvalue nicht zutrifft.

Ein lvalue kann nicht verschoben werden.You can't move an lvalue. Es gibt jedoch eine Art von glvalue (alle Werte mit einer Identität), der verschoben werden kann — wenn Sie diesen Vorgang bewusst ausführen (und u. a. darauf achten, nach dem Verschieben nicht mehr darauf zuzugreifen) — hierbei handelt es sich um den xvalue.But there is a kind of glvalue (the set of things with identity) that you can move—if you know what you're doing (including being careful not to access it after the move)—and that's the xvalue. Wir kommen auf dieses Konzept im Folgenden noch einmal zurück, wenn wir uns einen Gesamtüberblick über Wertekategorien verschaffen.We'll revisit this idea one more time below, when we look at the complete picture of value categories.

rvalue-Verweise und Verweis-BindungsregelnRvalue references, and reference-binding rules

Dieser Abschnitt enthält die Syntax für einen Verweis auf einen rvalue.This section introduces the syntax for a reference to an rvalue. Bisher liegt noch kein erschöpfendes Thema zum Verschieben und Weiterleiten vor, aber es kann festgestellt werden, dass rvalue-Verweise einen erforderlichen Bestandteil der Lösung dieser Probleme darstellen.We'll have to wait for another topic to go into a substantial treatment of moving and forwarding, but suffice to say that rvalue references are a necessary piece of the solution of those problems. Bevor wir rvalue-Verweise betrachten, müssen wir uns eingehender T&—mit dem beschäftigen, was wir eingangs einfach als „Verweis“ bezeichnet haben.Before we look at rvalue references, though, we first need to be clearer about T&—the thing we've formerly been calling just "a reference". Tatsächlich handelt es sich um einen „lvalue (non-const)-Verweis“, der auf einen Wert verweist, in den der Benutzer des Verweises schreiben kann.It's really "an lvalue (non-const) reference", which refers to an value to which the user of the reference can write.

template<typename T> T& get_by_lvalue_ref() { ... } // Get by lvalue (non-const) reference.
template<typename T> void set_by_lvalue_ref(T&) { ... } // Set by lvalue (non-const) reference.

Ein lvalue-Verweis kann an einen lvalue, jedoch nicht an einen rvalue gebunden werden.An lvalue reference can bind to an lvalue, but not to an rvalue.

Außerdem gibt es lvalue-const-Verweise (T const&), die auf Objekte verweisen, in die der Benutzer des Verweises nicht schreiben kann (z. B. eine Konstante).Then there are lvalue const references (T const&), which refer to objects to which the user of the reference can't write (for example, a constant).

template<typename T> T const& get_by_lvalue_cref() { ... } // Get by lvalue const reference.
template<typename T> void set_by_lvalue_cref(T const&) { ... } // Set by lvalue const reference.

Ein lvalue-const-Verweis kann an einen lvalue oder an einen rvalue gebunden werden.An lvalue const reference can bind to an lvalue or to an rvalue.

Die Syntax für einen Verweis auf einen rvalue vom Typ T lautet T&&.The syntax for a reference to an rvalue of type T is written as T&&. Ein rvalue-Verweis bezieht sich auf einen verschiebbaren Wert, d. h. einen Wert, dessen Inhalt nach seiner Verwendung nicht beibehalten werden muss (z. B. einen temporären Wert).An rvalue reference refers to a movable value—an value whose contents we don't need to preserve after we've used it (for example, a temporary). Da es nur darum geht, aus dem an einen rvalue-Verweis gebundenen Wert zu verschieben (und diesen dadurch zu ändern), gelten die Qualifizierer const und volatile (die auch als cv-Qualifizierer bezeichnet werden) nicht für rvalue-Verweise.Since the whole point is to move from (thereby modifying) the value bound to an rvalue reference, const and volatile qualifiers (also known as cv-qualifiers) don't apply to rvalue references.

template<typename T> T&& get_by_rvalue_ref() { ... } // Get by rvalue reference.
struct A { A(A&& other) { ... } }; // A move constructor takes an rvalue reference.

Ein rvalue-Verweis ist an einen rvalue-Wert gebunden.An rvalue reference binds to an rvalue. Tatsächlich wird ein rvalue im Rahmen einer Überladungsauflösung eher an einen rvalue-Verweis gebunden, als an einen lvalue-const-Verweis.In fact, in terms of overload resolution, an rvalue prefers to be bound to an rvalue reference than to an lvalue const reference. Ein rvalue-Verweis kann jedoch nicht an einen lvalue gebunden werden, da ein rvalue-Verweis bekanntlich auf einen Wert verweist, für dessen Inhalt angenommen wird, dass er nicht beibehalten werden muss (z. B. der Parameter für einen Verschiebekonstruktor).But an rvalue reference can't bind to an lvalue because, as we've said, an rvalue reference refers to a value whose contents it's assumed we don't need to preserve (say, the parameter for a move constructor).

Sie können auch einen rvalue übergeben, wenn ein ByVal-Argument erwartet wird; dies erfolgt über einen Kopiervorgang zur Bearbeitung (oder einen Kopiervorgang zur Verschiebung, wenn der rvalue ein xvalue ist).You can also pass an rvalue where a by-value argument is expected, via copy construction (or via move construction, if the rvalue is an xvalue).

Ein glvalue weist eine Identität auf; bei einem prvalue ist dies nicht der FallA glvalue has identity; a prvalue does not

Nun ist uns bekannt, welche Werte eine Identität haben.At this stage, we know what has identity. Außerdem wissen wir, welche Werte verschoben und welche nicht verschoben werden können.And we know what's movable and what isn't. Wir haben jedoch noch keinen Namen für die Menge der Werte, die keine Identität haben.But we haven't yet named the set of values that don't have identity. Diese werden als prvalue oder reiner rvalue bezeichnet.That set is known as the prvalue, or pure rvalue.

int& get_by_ref() { ... }
int get_by_val() { ... }

int main()
{
    int* addr3{ &(get_by_ref() + 1) }; // Error: get_by_ref() + 1 is a prvalue.
    int* addr4{ &get_by_val() }; // Error: get_by_val() is a prvalue.
}

Ein lvalue weist eine Identität auf; bei einem prvalue ist dies nicht der Fall

Kompletter Überblick über die WertekategorienThe complete picture of value categories

Nun müssen wir nur noch die obigen Informationen und Abbildungen zu einem einzigen großen Überblick kombinieren.It only remains to combine the info and illustrations above into a single, big picture.

Kompletter Überblick über die Wertekategorien

glvalue (i)glvalue (i)

Ein glvalue (generalisierter lvalue) weist eine Identität auf.A glvalue (generalized lvalue) has identity.

lvalue (i&!m)lvalue (i&!m)

Ein lvalue (eine Art von glvalue) weist eine Identität auf, kann jedoch nicht verschoben werden.An lvalue (a kind of glvalue) has identity, but isn't movable. Hierbei handelt es sich in der Regel um Werte mit Lese-/Schreibzugriff, die per Verweis bzw. const-Verweis oder per Wert übergeben werden, wenn Kopiervorgänge kostengünstig sind.These are typically read-write values that you pass around by reference or by const reference, or by value if copying is cheap. Ein lvalue kann nicht an einen rvalue-Verweis gebunden werden.An lvalue can't be bound to an rvalue reference.

xvalue (i&m)xvalue (i&m)

Ein xvalue (eine Art von glvalue, jedoch auch eine Art von rvalue) weist eine Identität auf, kann jedoch auch verschoben werden.An xvalue (a kind of glvalue, but also a kind of rvalue) has identity, and is also movable. Dies kann ein früherer lvalue sein, denn Sie nun verschieben, weil das Kopieren zu teuer ist; Sie müssen nun darauf achten, dass Sie später nicht wieder darauf zugreifen.This might be an erstwhile lvalue that you've decided to move because copying is expensive, and you'll be careful not to access it afterward. Im Folgenden wird erläutert, wie Sie einen lvalue in einen xvalue umwandeln können.Here's how you can turn an lvalue into an xvalue.

struct A { ... };
A a; // a is an lvalue...
static_cast<A&&>(a); // ...but this expression is an xvalue.

Im obigen Codebeispiel wurde noch nicht verschoben.In the code example above, we haven't moved anything yet. Wir haben lediglich einen xvalue erstellt, indem ein lvalue in einen unbenannten rvalue-Verweis umgewandelt wurde.We've just created an xvalue by casting an lvalue to an unnamed rvalue reference. Er kann weiterhin anhand seines lvalue-Namens erkannt werden; als xvalue ist es jedoch nun möglich, ihn zu verschieben.It can still be identified by its lvalue name; but, as an xvalue, it is now capable of being moved. Die Gründe für diese Vorgehensweise und der tatsächliche Ablauf des Verschiebens werden in einem künftigen Artikel erläutert.The reasons for doing so, and what moving actually looks like, will have to wait for another topic. Stellen Sie sich einfach vor, dass das „x“ in „xvalue“ für „ausschließlich für eXperten“ steht; dies mag fürs Erste genügen.But you can think of the "x" in "xvalue" as meaning "expert-only" if that helps. Durch das Umwandeln eines lvalue in einen xvalue (eine Art von rvalue), kann der erhaltene Wert an einen rvalue-Verweis gebunden werden.By casting an lvalue into an xvalue (a kind of rvalue), the value then becomes capable of being bound to an rvalue reference.

Hier sind zwei weitere Beispiele für xvalues: Aufrufen einer Funktion, die einen unbenannten rvalue-Verweis zurückgibt und Zugreifen auf einen Member eines xvalue.Here are two other examples of xvalues—calling a function that returns an unnamed rvalue reference, and accessing a member of an xvalue.

struct A { int m; };
A&& f();
f(); // This expression is an xvalue...
f().m; // ...and so is this.

prvalue (!i&m)prvalue (!i&m)

Ein prvalue (reiner rvalue; eine Art von rvalue) weist keine Identität auf, er kann jedoch verschoben werden.A prvalue (pure rvalue; a kind of rvalue) doesn't have identity, but is movable. Hierbei handelt es sich in der Regel um temporäre Werte, das Ergebnis des Aufrufs einer Funktion, deren Rückgabe nach Wert erfolgt oder das Ergebnis der Auswertung eines anderen Ausdrucks, der kein glvalue ist,These are typically temporaries, the result of calling a function that returns by value, or the result of evaluating any other expression that's not a glvalue,

rvalue (m)rvalue (m)

Ein rvalue kann verschoben werden.An rvalue is movable. Ein rvalue-Verweis bezieht sich stets auf einen rvalue (einen Wert, für dessen Inhalt angenommen wird, dass er nicht beibehalten werden muss).An rvalue reference always refers to an rvalue (a value whose contents it's assumed we don't need to preserve).

Ist aber ein rvalue-Verweis selbst ein rvalue?But, is an rvalue reference itself an rvalue? Ein unbenannter rvalue-Verweis (wie die in den obigen xvalue-Codebeispielen veranschaulichten) ist ein Wert. Es handelt sich also tatsächlich um einen rvalue.An unnamed rvalue reference (like the ones shown in the xvalue code examples above) is an xvalue so, yes, it's an rvalue. Er wird vorzugsweise an den Funktionsparameter eines rvalue-Verweises gebunden, z. B. an den eines Verschiebekonstruktors.It prefers to be bound to an rvalue reference function parameter, such as that of a move constructor. Umgekehrt (und vielleicht weniger intuitiv) gilt Folgendes: Wenn ein rvalue-Verweis einen Namen aufweist, ist der Ausdruck des betreffenden Namens ein lvalue.Conversely (and perhaps counter-intuitively), if an rvalue reference has a name, then the expression consisting of that name is an lvalue. Somit kann er nicht an den Parameter eines rvalue-Verweises gebunden werden.So it can't be bound to an rvalue reference parameter. Dies ist jedoch problemlos möglich. Wandeln Sie ihn einfach wieder in einen unbenannten rvalue-Verweis (einen xvalue) um.But it's easy to make it do so—just cast it to an unnamed rvalue reference (an xvalue) again.

void foo(A&) { ... }
void foo(A&&) { ... }
void bar(A&& a) // a is a named rvalue reference; it's an lvalue.
{
    foo(a); // Calls foo(A&).
    foo(static_cast<A&&>(a)); // Calls foo(A&&).
}
A&& get_by_rvalue_ref() { ... } // This unnamed rvalue reference is an xvalue.

!i&!m!i&!m

Die Art von Wert, die keine Identität aufweist und nicht verschoben werden kann, ist die einzige Kombination, die bisher noch nicht erörtert wurde.The kind of value that doesn't have identity and isn't movable is the one combination that we haven't yet discussed. Wir können sie jedoch nicht ignorieren, da diese Kategorie ein nützliches Konzept in C++ darstellt.But we can disregard it, because that category isn't a useful idea in the C++ language.

Reduzierungsregeln für VerweiseReference-collapsing rules

Mehrere gleichartige Verweise in einem Ausdruck (ein lvalue-Verweis auf einen lvalue-Verweis oder ein rvalue-Verweis auf einen rvalue-Verweis) heben einander auf.Multiple like references in an expression (an lvalue reference to an lvalue reference, or an rvalue reference to an rvalue reference) cancel one another out.

  • A& & wird reduziert zu A&.A& & collapses into A&.
  • A&& && wird reduziert zu A&&.A&& && collapses into A&&.

Mehrere unterschiedliche Verweise in einem Ausdruck werden zu einem lvalue-Verweis reduziert.Multiple unlike references in an expression collapse to an lvalue reference.

  • A& && wird reduziert zu A&.A& && collapses into A&.
  • A&& & wird reduziert zu A&.A&& & collapses into A&.

WeiterleitungsverweiseForwarding references

In diesem letzten Abschnitt werden die bereits erörterten rvalue-Verweise dem abweichenden Konzept eines Weiterleitungsverweises gegenübergestellt.This final section contrasts rvalue references, which we've already discussed, with the different concept of a forwarding reference.

void foo(A&& a) { ... }
  • Wie wir festgestellt haben, ist A&& ein rvalue-Verweis.A&& is an rvalue reference, as we've seen. Die Typen const und volatile gelten nicht für rvalue-Verweise.Const and volatile don't apply to rvalue references.
  • foo akzeptiert nur rvalues vom Typ A.foo accepts only rvalues of type A.
  • rvalue-Verweise (wie A&&) gibt es, damit Sie eine Überladung schreiben können, die für den Fall der Übergabe eines temporären Werts (oder sonstigen rvalue) optimiert ist.The reason rvalue references (such as A&&) exist is so that you can author an overload that's optimized for the case of a temporary (or other rvalue) being passed.
template <typename _Ty> void bar(_Ty&& ty) { ... }
  • _Ty&& ist ein Weiterleitungsverweis._Ty&& is a forwarding reference. Je nachdem, was Sie an bar übergeben, kann der Typ _Ty const/non-const sein, unabhängig von volatile/non-volatile.Depending what you pass to bar, type _Ty could be const/non-const independently of volatile/non-volatile.
  • bar akzeptiert beliebige lvalues und rvalues vom Typ _Ty.bar accepts any lvalue or rvalue of type _Ty.
  • Durch das Übergeben eines lvalue wird der Weiterleitungsverweis zu _Ty& &&, wodurch der lvalue-Verweis _Ty& reduziert wird.Passing an lvalue causes the forwarding reference to become _Ty& &&, which collapses to the lvalue reference _Ty&.
  • Durch das Übergeben eines rvalue wird der Weiterleitungsverweis zu _Ty&& &&, wodurch der rvalue-Verweis _Ty&& reduziert wird.Passing an rvalue causes the forwarding reference to become _Ty&& &&, which collapses to the rvalue reference _Ty&&.
  • Weiterleitungsverweise (wie _Ty&&) gibt es nicht aus Optimierungsgründen. Stattdessen sollen sie übergebene Werte annehmen und transparent und effizient weiterleiten.The reason forwarding references (such as _Ty&&) exist is not for optimization, but to take what you pass to them and to forward it on transparently and efficiently. Einen Weiterleitungsverweis treffen Sie wahrscheinlich nur an, wenn Sie Bibliothekscode schreiben (oder eingehender untersuchen), beispielsweise eine Factory-Funktion, mit der die Weiterleitung für Konstruktorargumente erfolgt.You're likely to encounter a forwarding reference only if you write (or closely study) library code—for example, a factory function that forwards on constructor arguments.

QuellenSources

  • [Stroustrup, 2013] B. Stroustrup: The C++ Programming Language, Fourth Edition.[Stroustrup, 2013] B. Stroustrup: The C++ Programming Language, Fourth Edition. Addison-Wesley.Addison-Wesley. 2013.2013.