Kurzübersicht über C ++ - Wertkategorien: Teil 1

Bild

Ziel dieser Kurzreferenz ist es, Informationen zu Wertkategorien in C ++, Zuweisung, Parameterübergabe und Rückgabe von Funktionen an einem Ort zu sammeln und zu organisieren. Ich habe versucht, diese Kurzreferenz bequem zu gestalten, um eine der möglichen Lösungen schnell vergleichen und auswählen zu können. Aus diesem Grund habe ich hier mehrere Tabellen erstellt.


Verwenden Sie zur Einführung in das Thema die folgenden Links:


C ++ rWertreferenzen und Verschiebungssemantik für Anfänger
R-Werte neu definiert
C ++ bewegt sich für Leute, die nicht wissen oder sich nicht darum kümmern, was Werte sind
Scott Meyers. Effektives modernes C ++. 2015
Grundlegendes zur Bewegungssemantik und perfekten Weiterleitung: Teil 1
Grundlegendes zur Bewegungssemantik und perfekten Weiterleitung: Teil 2
Grundlegendes zur Bewegungssemantik und perfekten Weiterleitung: Teil 3
Müssen wir die Zuordnung verschieben und kopieren?


Wertkategorien


Bild


Ab C ++ 11 kann jeder Ausdruck nur einer von drei Wertkategorien angehören: lvalue, xvalue oder prvalue:


  • xvalue (Expiring Value) - Ausdruck, der ein nicht temporäres Objekt identifiziert, das verschoben werden kann
  • lvalue (linker Wert) - Ausdruck, der ein nicht temporäres Funktionsobjekt kennzeichnet, das nicht verschoben werden kann
  • prvalue (reiner Wert) - Ausdruck, der ein Objekt initialisiert

Bild


Diese drei Kategorien können in drei überlappende Gruppen eingeteilt werden:


  • glvalue (generalized lvalue) gruppiert lvalue und xvalue. glvalue hat Adresse im Speicher (hat Identität) und kann daher normalerweise einen Wert zugewiesen bekommen (wenn es nicht const ist)
  • rvalue gruppiert prvalue und xvalue. rvalue kann entweder verschoben werden (xvalue) oder gehört überhaupt nicht zu einem vorhandenen Objekt (prvalue). rvalue kann an Verschiebungskonstruktoren, Verschiebungszuweisungsoperatoren oder Verschiebungsfunktionen übergeben werden

Xvalue hat also eine Adresse im Speicher und kann verschoben werden.


Bild


Sie können der obigen Tabelle entnehmen, dass benannte Variablen der Kategorien lvalue, lvalue reference und rvalue reference dieselbe Ausdruckskategorie haben: lvalue (rot hervorgehoben). Dies bedeutet zum Beispiel, dass beim Übergeben einer rWertreferenz an eine Funktion eine lWertreferenzüberlastung gewählt wird: T&& x=T(); f(x); T&& x=T(); f(x);


Links:
C ++ lvalue rvalue xvalue glvalue prvalue
Wertkategorien in C ++ 17
Wertkategorien


Abkürzungen in diesem Artikel


Abkürzungen von Konstruktoren, Operatoren und Destruktoren:


  • Dc - Standardkonstruktor
  • Pc - Parametrisierter Konstruktor
  • Cc - Kopierkonstruktor
  • Ca - Zuweisung kopieren (kann zugewiesenen Speicher wiederverwenden, z. B. für std :: string)
  • Mc - Konstruktor verschieben
  • Ma - Zuweisung verschieben
  • Va - Konvertierungszuweisungsoperator
  • D - Zerstörer

Abkürzungen von Wertkategorien:


  • LV - L-Wert: T LV;
  • LR - LWertreferenz: T& LR = LV;
  • XV - X-Wert: move(LV)
  • PRV - reiner Wert. Literal oder Ergebnis eines Funktionsaufrufs, dessen Rückgabetyp keine Referenz ist: T f() { return T(); } T f() { return T(); }
  • FR - Weiterleitungsreferenz: auto&& FR = LV;
  • CLV - const LV. CLR - const LR. CXV - const XV.
  • CPRV - const PRV. CPRV gibt es nur für Klassen und Arrays. Nicht-Klassen-CPRV ohne Array wie cont int werden implizit in PRV wie int konvertiert, z. B. im Rückgabewert der Funktion const int f() )
  • RV ist XV oder PRV
  • CRV ist CXV oder CPRV

Andere Abkürzungen:


  • PF - Perfekte Weiterleitung (unter Verwendung der Referenz für die Weiterleitung von Vorlagen, um Parameter an die Funktion zu übergeben)

Wertkategorien zuweisen


Die Zuordnung von Wertkategorien kann als Grundlage für alle folgenden Abschnitte angesehen werden. Bei expliziter Zuordnung einer Kategorie zu einer anderen ohne explizite Kategoriewandlung findet eine implizite Wandlung statt: FROM_TYPE x; TO_TYPE y; y = x; FROM_TYPE x; TO_TYPE y; y = x; In der folgenden Tabelle sehen Sie, was passiert, wenn Ausdruck vom Typ From einer Variablen vom Typ To zugewiesen wird:


  • Ein Minuszeichen bedeutet, dass eine solche Zuordnung nicht möglich ist
  • Ein Pluszeichen bedeutet, dass eine solche Zuordnung möglich ist und kein Aufruf von Kopier- / Verschiebungsoperatoren oder Konstruktoren erforderlich ist.
  • In anderen Fällen gibt es Text, dh, welche Kopier- / Verschiebungsoperatoren und Konstruktoren während einer solchen Zuweisung aufgerufen werden.

Konvertierungen, die nicht implizit von der zurückgegebenen Ausdruckskategorie zum Funktionsrückgabewerttyp erfolgen können, sind mit roter Schrift gekennzeichnet. Weitere Informationen finden Sie im Abschnitt "Zurückgeben von einer Funktion".
Linien mit konstanten Typen werden blau hervorgehoben.


Bild


Fußnoten:


1 - Const auto && -Referenzen sind keine Weiterleitungsreferenzen, sondern const rvalue -Referenzen. Sie verhalten sich also wie const T &&.
2 - Ein temporäres Objekt, das zur Speicherung eines Referenzinitialisierers erstellt wurde, bleibt bis zum Ende des Gültigkeitsbereichs der Referenz bestehen (siehe nächster Abschnitt). Funktioniert nicht zum Konvertieren des Rückgabewerts in einen Funktionstyp.
3 - Wenn ein Literal übergeben wird, muss rechts ein Typspezifizierer für auto angegeben werden: auto&& = T{2};


Hinweis:


  • Nicht konstante lWert- und rWert-Referenzen können keine konstanten Typen akzeptieren. Aber non-const non-reference type kann const-Typen akzeptieren.
  • Die R-Wert-Referenz (T &&) kann x-Wert oder pr-Wert (XV oder PRV) akzeptieren.
  • Lvalue reference (T &) kann lvalue oder lvalue reference (LV oder LR) akzeptieren
  • Nicht-Referenztypen akzeptieren alle Typen durch Konvertieren, Kopieren, Verschieben oder RVO
  • Konstantwertreferenzen (const T &, const auto &) und automatische Weiterleitungsreferenzen (auto &&) akzeptieren alle Typen ohne Kopieren oder Verschieben
  • Konstante und nicht konstante Wertereferenzen (T &&, const T &&) können keine konstanten oder nicht konstanten Werte (LV oder CLV) akzeptieren.
  • LR kann keine konstanten oder nicht konstanten Wertereferenzen akzeptieren (XV oder PRV oder CXV oder CPRV). CLR kann sie akzeptieren.
  • Typen mit const-Modifikatoren können keinen Typen ohne const-Modifikatoren zugewiesen werden (mit Ausnahme von auto, das zu const werden kann, wenn const auf der rechten Seite explizit angegeben ist).
  • Die Übergabe von prvalue (Übergabe von Rückgabewert, Literal oder Konstruktorergebnis) entspricht der Übergabe von xvalue, vermeidet jedoch den Aufruf von move constructor, wenn Sie einen Nicht-Referenztyp zuweisen
  • Die Übergabe von const prvalue (Übergabe des Rückgabewerts der Funktion const type) entspricht der Übergabe von const xvalue, vermeidet jedoch den Aufruf des Kopierkonstruktors bei der Zuweisung zu einem Nichtreferenztyp
  • Autoverweise können zu const werden, sodass sie sowohl const- als auch non-const-Varianten akzeptieren können. Sie können auch zu konstanten Referenzen werden, damit die temporäre Speicherung bis zum Ende des Gültigkeitsbereichs der Referenz fortgesetzt wird. Auto * -Verweise können jedoch nicht zu const werden, wenn dies rechts nicht explizit angegeben ist. Aus diesem Grund können Sie Auto & keinen Wert zuweisen.
  • Const-Referenz kann ein temporäres Objekt empfangen und bis zum Ende des Referenzbereichs beibehalten (Nicht-Const-Referenz kann nicht). Dies funktioniert nur innerhalb des aktuellen Bereichs (lokale Funktionsreferenzen und Funktionsargumente). Wenn reference ein Mitglied einer Klasse ist, wird das temporäre Objekt am Ende des Konstruktors oder der Funktion zerstört, und reference zeigt weiterhin auf ein zerstörtes Objekt, bei dem es sich um ein undefiniertes Verhalten handelt.
  • Auto x = CLV; wird automatisch auf Nicht-Konstant-Typ ableiten.

Beispiele und Tests mit Drucken jedes aufgerufenen Konstruktors und Operators
 #include <iostream> #include <iomanip> #include <map> #include <vector> #include <string> using namespace std; template<class C, class T> auto contains(const C& v, const T& x) -> decltype(end(v), true) { return end(v) != std::find(begin(v), end(v), x); } template <class... Types> constexpr inline __attribute__((__always_inline__)) int UNUSED(Types&&...) { return 0; }; map<string, map<string, string>> res; vector<string> froms; vector<string> tos; string from; string to; void report(string st) { if (!from.empty() && !to.empty()) { res[from][to] += st; } cout << st << " "; } struct T { T() { report("Dc"); } T(int va) : a(va) { report("Pc"); } T(const T& other) : a(other.a) { report("Cc"); } T(T&& other) : a(std::exchange(other.a, 0)) { report("Mc"); } T& operator=(int va) { report("Va"); a = va; return *this; } T& operator=(const T& rhs) { report("Ca"); // check for self-assignment if(&rhs == this) return *this; a = rhs.a; return *this; } T& operator=(T&& rhs) { report("Ma"); // check for self-assignment if(&rhs == this) return *this; a = std::exchange(rhs.a, 0); return *this; } ~T() { report("D"); } int a = 1; }; T Fprv() { return T(); } const T Fcprv() { return T(); } void print_col(const string &st, int width) { cout << endl << left << setw(width) << st; } void test_assign(string lto, string lfrom) { from = lfrom; to = lto; res[from][to] = ""; if (!from.empty() && !to.empty()) { if (!contains(froms, from)) froms.push_back(from); if (!contains(tos, to)) tos.push_back(to); } print_col(lto + " = " + lfrom + ": ", 20); } #define TEST_ASSIGN(t, v) { \ test_assign(#t, #v); \ ts = v; \ cout << sa; \ UNUSED(s); \ cout << "-"; \ } void test_conversion() { T l; const T cl; T& lr = l; const T& clr = l; T&& rr = T(); const T&& crr = T(); auto &&fr = T(); TEST_ASSIGN(T, 8); TEST_ASSIGN(T, T()); TEST_ASSIGN(T, l); TEST_ASSIGN(T, move(l)); TEST_ASSIGN(T, cl); TEST_ASSIGN(T, move(cl)); TEST_ASSIGN(T, lr); TEST_ASSIGN(T, move(lr)); TEST_ASSIGN(T, clr); TEST_ASSIGN(T, move(clr)); TEST_ASSIGN(T, rr); TEST_ASSIGN(T, move(rr)); TEST_ASSIGN(T, crr); TEST_ASSIGN(T, move(crr)); TEST_ASSIGN(T, Fcprv()); TEST_ASSIGN(T, Fprv()); TEST_ASSIGN(T, fr); TEST_ASSIGN(T, move(fr)); TEST_ASSIGN(const T, 8); TEST_ASSIGN(const T, T()); TEST_ASSIGN(const T, l); TEST_ASSIGN(const T, move(l)); TEST_ASSIGN(const T, cl); TEST_ASSIGN(const T, move(cl)); TEST_ASSIGN(const T, lr); TEST_ASSIGN(const T, move(lr)); TEST_ASSIGN(const T, clr); TEST_ASSIGN(const T, move(clr)); TEST_ASSIGN(const T, rr); TEST_ASSIGN(const T, move(rr)); TEST_ASSIGN(const T, crr); TEST_ASSIGN(const T, move(crr)); TEST_ASSIGN(const T, Fcprv()); TEST_ASSIGN(const T, Fprv()); TEST_ASSIGN(const T, fr); TEST_ASSIGN(const T, move(fr)); //TEST_ASSIGN(T&, 8); //TEST_ASSIGN(T&, T()); TEST_ASSIGN(T&, l); //TEST_ASSIGN(T&, move(l)); //TEST_ASSIGN(T&, cl); //TEST_ASSIGN(T&, move(cl)); TEST_ASSIGN(T&, lr); //TEST_ASSIGN(T&, move(lr)); //TEST_ASSIGN(T&, clr); //TEST_ASSIGN(T&, move(clr)); //TEST_ASSIGN(T&, rr); //TEST_ASSIGN(T&, move(rr)); //TEST_ASSIGN(T&, crr); //TEST_ASSIGN(T&, move(crr)); //TEST_ASSIGN(T&, Fcprv()); //TEST_ASSIGN(T&, Fprv()); TEST_ASSIGN(T&, fr); //TEST_ASSIGN(T&, move(fr)); TEST_ASSIGN(const T&, 8); TEST_ASSIGN(const T&, T()); TEST_ASSIGN(const T&, l); TEST_ASSIGN(const T&, move(l)); TEST_ASSIGN(const T&, cl); TEST_ASSIGN(const T&, move(cl)); TEST_ASSIGN(const T&, lr); TEST_ASSIGN(const T&, move(lr)); TEST_ASSIGN(const T&, clr); TEST_ASSIGN(const T&, move(clr)); TEST_ASSIGN(const T&, rr); TEST_ASSIGN(const T&, move(rr)); TEST_ASSIGN(const T&, crr); TEST_ASSIGN(const T&, move(crr)); TEST_ASSIGN(const T&, Fcprv()); TEST_ASSIGN(const T&, Fprv()); TEST_ASSIGN(const T&, fr); TEST_ASSIGN(const T&, move(fr)); TEST_ASSIGN(T&&, 8); TEST_ASSIGN(T&&, T()); //TEST_ASSIGN(T&&, l); TEST_ASSIGN(T&&, move(l)); //TEST_ASSIGN(T&&, cl); //TEST_ASSIGN(T&&, move(cl)); //TEST_ASSIGN(T&&, lr); TEST_ASSIGN(T&&, move(lr)); //TEST_ASSIGN(T&&, clr); //TEST_ASSIGN(T&&, move(clr)); //TEST_ASSIGN(T&&, rr); TEST_ASSIGN(T&&, move(rr)); //TEST_ASSIGN(T&&, crr); //TEST_ASSIGN(T&&, move(crr)); //TEST_ASSIGN(T&&, Fcprv()); TEST_ASSIGN(T&&, Fprv()); //TEST_ASSIGN(T&&, fr); TEST_ASSIGN(T&&, move(fr)); TEST_ASSIGN(const T&&, 8); TEST_ASSIGN(const T&&, T()); //TEST_ASSIGN(const T&&, l); TEST_ASSIGN(const T&&, move(l)); //TEST_ASSIGN(const T&&, cl); TEST_ASSIGN(const T&&, move(cl)); //TEST_ASSIGN(const T&&, lr); TEST_ASSIGN(const T&&, move(lr)); //TEST_ASSIGN(const T&&, clr); TEST_ASSIGN(const T&&, move(clr)); //TEST_ASSIGN(const T&&, rr); TEST_ASSIGN(const T&&, move(rr)); //TEST_ASSIGN(const T&&, crr); TEST_ASSIGN(const T&&, move(crr)); TEST_ASSIGN(const T&&, Fcprv()); TEST_ASSIGN(const T&&, Fprv()); //TEST_ASSIGN(const T&&, fr); TEST_ASSIGN(const T&&, move(fr)); //TEST_ASSIGN(auto&, T{8}); //TEST_ASSIGN(auto&, T()); TEST_ASSIGN(auto&, l); //TEST_ASSIGN(auto&, move(l)); TEST_ASSIGN(auto&, cl); TEST_ASSIGN(auto&, move(cl)); TEST_ASSIGN(auto&, lr); //TEST_ASSIGN(auto&, move(lr)); TEST_ASSIGN(auto&, clr); TEST_ASSIGN(auto&, move(clr)); TEST_ASSIGN(auto&, rr); //TEST_ASSIGN(auto&, move(rr)); TEST_ASSIGN(auto&, crr); TEST_ASSIGN(auto&, move(crr)); TEST_ASSIGN(auto&, Fcprv()); //TEST_ASSIGN(auto&, Fprv()); TEST_ASSIGN(auto&, fr); //TEST_ASSIGN(auto&, move(fr)); TEST_ASSIGN(const auto&, T{8}); TEST_ASSIGN(const auto&, T()); TEST_ASSIGN(const auto&, l); TEST_ASSIGN(const auto&, move(l)); TEST_ASSIGN(const auto&, cl); TEST_ASSIGN(const auto&, move(cl)); TEST_ASSIGN(const auto&, lr); TEST_ASSIGN(const auto&, move(lr)); TEST_ASSIGN(const auto&, clr); TEST_ASSIGN(const auto&, move(clr)); TEST_ASSIGN(const auto&, rr); TEST_ASSIGN(const auto&, move(rr)); TEST_ASSIGN(const auto&, crr); TEST_ASSIGN(const auto&, move(crr)); TEST_ASSIGN(const auto&, Fcprv()); TEST_ASSIGN(const auto&, Fprv()); TEST_ASSIGN(const auto&, fr); TEST_ASSIGN(const auto&, move(fr)); TEST_ASSIGN(auto&&, T{8}); TEST_ASSIGN(auto&&, T()); TEST_ASSIGN(auto&&, l); TEST_ASSIGN(auto&&, move(l)); TEST_ASSIGN(auto&&, cl); TEST_ASSIGN(auto&&, move(cl)); TEST_ASSIGN(auto&&, lr); TEST_ASSIGN(auto&&, move(lr)); TEST_ASSIGN(auto&&, clr); TEST_ASSIGN(auto&&, move(clr)); TEST_ASSIGN(auto&&, rr); TEST_ASSIGN(auto&&, move(rr)); TEST_ASSIGN(auto&&, crr); TEST_ASSIGN(auto&&, move(crr)); TEST_ASSIGN(auto&&, Fcprv()); TEST_ASSIGN(auto&&, Fprv()); TEST_ASSIGN(auto&&, fr); TEST_ASSIGN(auto&&, move(fr)); TEST_ASSIGN(const auto&&, T{8}); TEST_ASSIGN(const auto&&, T()); //TEST_ASSIGN(const auto&&, l); TEST_ASSIGN(const auto&&, move(l)); //TEST_ASSIGN(const auto&&, cl); TEST_ASSIGN(const auto&&, move(cl)); //TEST_ASSIGN(const auto&&, lr); TEST_ASSIGN(const auto&&, move(lr)); //TEST_ASSIGN(const auto&&, clr); TEST_ASSIGN(const auto&&, move(clr)); //TEST_ASSIGN(const auto&&, rr); TEST_ASSIGN(const auto&&, move(rr)); //TEST_ASSIGN(const auto&&, crr); TEST_ASSIGN(const auto&&, move(crr)); TEST_ASSIGN(const auto&&, Fcprv()); TEST_ASSIGN(const auto&&, Fprv()); //TEST_ASSIGN(const auto&&, fr); TEST_ASSIGN(const auto&&, move(fr)); cout << endl; const int twidth = 9; cout << left << setw(twidth) << "From:"; for (const auto& lto : tos) { cout << left << setw(twidth) << lto; } cout << endl; for (const auto& lfrom : froms) { cout << left << setw(twidth) << lfrom; for (const auto& lto : tos) { if (!res.count(lfrom) || !res[lfrom].count(lto)) { cout << left << setw(twidth) << "-"; } else if (res[lfrom][lto].empty()) { cout << left << setw(twidth) << "+"; } else { cout << left << setw(twidth) << res[lfrom][lto]; } } cout << endl; } cout << endl; } int main() { test_conversion(); cout << endl; return 0; } /* Output: Dc Dc Dc Dc Dc T = 8: Pc 8-D T = T(): Dc 1-D T = l: Cc 1-D T = move(l): Mc 1-D T = cl: Cc 1-D T = move(cl): Cc 1-D T = lr: Cc 0-D T = move(lr): Mc 0-D T = clr: Cc 0-D T = move(clr): Cc 0-D T = rr: Cc 1-D T = move(rr): Mc 1-D T = crr: Cc 1-D T = move(crr): Cc 1-D T = Fcprv(): Dc 1-D T = Fprv(): Dc 1-D T = fr: Cc 1-D T = move(fr): Mc 1-D const T = 8: Pc 8-D const T = T(): Dc 1-D const T = l: Cc 0-D const T = move(l): Mc 0-D const T = cl: Cc 1-D const T = move(cl): Cc 1-D const T = lr: Cc 0-D const T = move(lr): Mc 0-D const T = clr: Cc 0-D const T = move(clr): Cc 0-D const T = rr: Cc 0-D const T = move(rr): Mc 0-D const T = crr: Cc 1-D const T = move(crr): Cc 1-D const T = Fcprv(): Dc 1-D const T = Fprv(): Dc 1-D const T = fr: Cc 0-D const T = move(fr): Mc 0-D T& = l: 0- T& = lr: 0- T& = fr: 0- const T& = 8: Pc 8-D const T& = T(): Dc 1-D const T& = l: 0- const T& = move(l): 0- const T& = cl: 1- const T& = move(cl): 1- const T& = lr: 0- const T& = move(lr): 0- const T& = clr: 0- const T& = move(clr): 0- const T& = rr: 0- const T& = move(rr): 0- const T& = crr: 1- const T& = move(crr): 1- const T& = Fcprv(): Dc 1-D const T& = Fprv(): Dc 1-D const T& = fr: 0- const T& = move(fr): 0- T&& = 8: Pc 8-D T&& = T(): Dc 1-D T&& = move(l): 0- T&& = move(lr): 0- T&& = move(rr): 0- T&& = Fprv(): Dc 1-D T&& = move(fr): 0- const T&& = 8: Pc 8-D const T&& = T(): Dc 1-D const T&& = move(l): 0- const T&& = move(cl): 1- const T&& = move(lr): 0- const T&& = move(clr): 0- const T&& = move(rr): 0- const T&& = move(crr): 1- const T&& = Fcprv(): Dc 1-D const T&& = Fprv(): Dc 1-D const T&& = move(fr): 0- auto& = l: 0- auto& = cl: 1- auto& = move(cl): 1- auto& = lr: 0- auto& = clr: 0- auto& = move(clr): 0- auto& = rr: 0- auto& = crr: 1- auto& = move(crr): 1- auto& = Fcprv(): Dc 1-D auto& = fr: 0- const auto& = T{8}: Pc 8-D const auto& = T(): Dc 1-D const auto& = l: 0- const auto& = move(l): 0- const auto& = cl: 1- const auto& = move(cl): 1- const auto& = lr: 0- const auto& = move(lr): 0- const auto& = clr: 0- const auto& = move(clr): 0- const auto& = rr: 0- const auto& = move(rr): 0- const auto& = crr: 1- const auto& = move(crr): 1- const auto& = Fcprv(): Dc 1-D const auto& = Fprv(): Dc 1-D const auto& = fr: 0- const auto& = move(fr): 0- auto&& = T{8}: Pc 8-D auto&& = T(): Dc 1-D auto&& = l: 0- auto&& = move(l): 0- auto&& = cl: 1- auto&& = move(cl): 1- auto&& = lr: 0- auto&& = move(lr): 0- auto&& = clr: 0- auto&& = move(clr): 0- auto&& = rr: 0- auto&& = move(rr): 0- auto&& = crr: 1- auto&& = move(crr): 1- auto&& = Fcprv(): Dc 1-D auto&& = Fprv(): Dc 1-D auto&& = fr: 0- auto&& = move(fr): 0- const auto&& = T{8}: Pc 8-D const auto&& = T(): Dc 1-D const auto&& = move(l): 0- const auto&& = move(cl): 1- const auto&& = move(lr): 0- const auto&& = move(clr): 0- const auto&& = move(rr): 0- const auto&& = move(crr): 1- const auto&& = Fcprv(): Dc 1-D const auto&& = Fprv(): Dc 1-D const auto&& = move(fr): 0- From: T const T T& const T& T&& const T&&auto& const auto&auto&& const auto&& 8 PcD PcD - PcD PcD PcD - - - - T() DcD DcD - DcD DcD DcD - DcD DcD DcD l CcD CcD + + - - + + + - move(l) McD McD - + + + - + + + cl CcD CcD - + - - + + + - move(cl) CcD CcD - + - + + + + + lr CcD CcD + + - - + + + - move(lr) McD McD - + + + - + + + clr CcD CcD - + - - + + + - move(clr)CcD CcD - + - + + + + + rr CcD CcD - + - - + + + - move(rr) McD McD - + + + - + + + crr CcD CcD - + - - + + + - move(crr)CcD CcD - + - + + + + + Fcprv() DcD DcD - DcD - DcD DcD DcD DcD DcD Fprv() DcD DcD - DcD DcD DcD - DcD DcD DcD fr CcD CcD + + - - + + + - move(fr) McD McD - + + + - + + + T{8} - - - - - - - PcD PcD PcD DDDDD */ 

Konstante Referenzen mit temporären Objekten initialisieren


Mit C ++ kann eine konstante Referenz mit einem temporären Objekt initialisiert werden. In diesem Fall wird die Lebensdauer eines temporären Objekts verlängert. Beispiel:


 struct T { int i = 1; }; const T& t = T(); cout << ti; 

Diese Verlängerung der Lebensdauer funktioniert jedoch nur bis zum Ende des Blocks, an dem das temporäre Objekt erstellt wurde. Wenn ein konstantes Referenzelement einer Klasse mit einem temporären Objekt in einem Konstruktor initialisiert wird, wird aus diesem Grund das temporäre Objekt am Ende des Konstruktors zerstört, und die Referenz verweist weiterhin auf ein zerstörtes Objekt, bei dem es sich um ein undefiniertes Verhalten handelt:


 class A { public: // Will not compile: value-initialization of reference type //A() : t() {} const T& t; }; class B { public: // Will compile in some compilers, but temporary object will be destructed at the end of constructor B() : t(T()) { cout << "In constructor: " << ti << endl; } const T& t; }; class C { public: // Will compile, but temporary object will be destructed at the end of constructor // Address sanitizer will show the problem C() : t(std::move(T())) { cout << "In constructor: " << ti << endl; } const T& t; }; C c; cout << "C: " << cti << endl; 

Ohne Adressbereinigung gibt dieses Programm etwas Müll aus, und mit Adressbereinigung wird ein Fehler angezeigt. Aus diesem Grund sollte diese C ++ - Funktion nicht oder nur mit Vorsicht verwendet werden.


Links:
Referenzinitialisierung
Konst Verweise auf temporäre Objekte
Informationen zum Binden einer const-Referenz an ein Unterobjekt eines temporären Objekts




Fahren Sie mit Teil 2 fort

Source: https://habr.com/ru/post/de479342/


All Articles