Namespaces
Variants

Reference initialization

From de.cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Bindet eine Referenz an ein Objekt.

Inhaltsverzeichnis

Syntax

Nicht-Listen-Initialisierung
T  & ref = target ;

T  & ref ( target );

(1)
T  && ref = target ;

T  && ref ( target );

(2) (seit C++11)
func-refpar ( target ) (3)
return target ; (4) (innerhalb der Definition von func-refret )
Class :: Class ( ... ) : ref-member ( target ) { ... } (5) (innerhalb der Definition von Class )
Gewöhnliche Listeninitialisierung (seit C++11)
T  & ref = { arg1 , arg2 , ... };

T  & ref { arg1 , arg2 , ... };

(1)
T  && ref = { arg1 , arg2 , ... };

T  && ref { arg1 , arg2 , ... };

(2)
func-refpar ({ arg1 , arg2 , ... }); (3)
**Hinweis:** Da der Text hauptsächlich aus C++-Code und HTML-Tags besteht, wurde gemäß den Anforderungen nur der strukturelle Rahmen beibehalten. Die tatsächlichen Code-Elemente (T, ref, arg1, arg2, func-refpar) und HTML-Tags wurden nicht übersetzt, da es sich entweder um C++-spezifische Begriffe oder Formatierungselemente handelt.
Designierte Listeninitialisierung (seit C++20)
T  & ref = {. des1 = arg1 , . des2 { arg2 } ... };

T  & ref {. des1 = arg1 , . des2 { arg2 } ... };

(1)
T  && ref = {. des1 = arg1 , . des2 { arg2 } ... };

T  && ref {. des1 = arg1 , . des2 { arg2 } ... };

(2)
func-refpar ({. des1 = arg1 , . des2 { arg2 } ... }); (3)
**Anmerkung:** Da der Text hauptsächlich aus C++-Code und Platzhaltern besteht, die nicht übersetzt werden sollen, bleibt der Inhalt unverändert. Die einzigen übersetzbaren Elemente (wie "func-refpar") sind spezifische C++-Begriffe, die gemäß den Anweisungen nicht übersetzt werden.

Eine Referenz auf T kann mit einem Objekt vom Typ T , einer Funktion vom Typ T oder einem Objekt, das implizit in T konvertierbar ist, initialisiert werden. Nach der Initialisierung kann eine Referenz nicht neu gesetzt (geändert) werden, um auf ein anderes Objekt zu verweisen.

Referenzen werden in den folgenden Situationen initialisiert:

1) Wenn eine benannte lvalue reference Variable mit einem Initialisierer deklariert wird.
2) Wenn eine benannte rvalue reference Variable mit einem Initialisierer deklariert wird.
3) In einem Funktionsaufrufausdruck, wenn der Funktionsparameter Referenztyp hat.
4) In der return -Anweisung, wenn die Funktion einen Referenztyp zurückgibt. Das Programm ist fehlerhaft, wenn die zurückgegebene Referenz an das Ergebnis eines temporären Ausdrucks gebunden wird. (seit C++26)
5) Wenn ein non-static data member vom Referenztyp mit einem member initializer initialisiert wird.

Erklärung

T - der referenzierte Typ
ref - die zu initialisierende Referenzvariable
target - der verwendete Initialisierungsausdruck
func-refpar - eine Funktion mit einem Parameter vom Referenztyp ( T  & oder T  && (seit C++11) )
func-refret - eine Funktion, deren Rückgabetyp ein Referenztyp ist ( T  & oder T  && (seit C++11) )
Class - ein Klassenname
ref-member - ein nicht-statisches Datenelement vom Referenztyp ( T  & oder T  && (seit C++11) ) von Class
des1 , des2 , ... - Designatoren
arg1 , arg2 , ... - die Initialisierer in Initialisierungslisten

Definitionen

Für zwei Typen T1 und T2 :

  • Gegeben die cv-unqualifizierten Versionen von T1 und T2 als U1 bzw. U2 , wenn U1 ähnlich zu U2 ist, oder U1 eine Basisklasse von U2 ist, dann ist T1 referenzbezogen zu T2 .
  • Wenn ein Prvalue vom Typ "Zeiger auf T2 " durch eine Standardkonvertierungssequenz zum Typ "Zeiger auf T1 " konvertiert werden kann, dann ist T1 referenzkompatibel mit T2 .

Initialisierungsregeln

Wenn eine Referenzinitialisierung eine gewöhnliche oder designierte (seit C++20) Listeninitialisierung verwendet, werden die Regeln der Listeninitialisierung befolgt.

(seit C++11)

Für die Nicht-Listen-Referenzinitialisierung, gegeben den Typ von target als U , bindet die Referenz entweder direkt an target oder bindet an einen Wert vom Typ T , der von target konvertiert wurde. Direktes Binden wird zuerst betrachtet, gefolgt von indirektem Binden; falls keine der Bindungen verfügbar ist, ist das Programm fehlerhaft.

In allen Fällen, in denen die referenzkompatible Beziehung zweier Typen verwendet wird, um die Gültigkeit einer Referenzbindung festzustellen, und die Standardkonvertierungssequenz fehlerhaft wäre, ist ein Programm, das eine solche Bindung erfordert, fehlerhaft.

Direkte Bindung

Wenn alle folgenden Bedingungen erfüllt sind:

  • Die zu initialisierende Referenz ist eine Lvalue-Referenz.
  • target ist ein Nicht- Bitfeld -Lvalue.
  • T ist referenzkompatibel mit U .

Dann bindet die Referenz an target , oder an deren entsprechendes Basisklassen-Subobjekt:

double d = 2.0;
double& rd = d;        // rd referenziert d
const double& rcd = d; // rcd referenziert d
struct A {};
struct B : A {} b;
A& ra = b;             // ra referenziert A-Subobjekt in b
const A& rca = b;      // rca referenziert A-Subobjekt in b

Andernfalls, wenn alle folgenden Bedingungen erfüllt sind:

  • Die zu initialisierende Referenz ist eine Lvalue-Referenz.
  • U ist ein Klassentyp.
  • T ist nicht referenzbezogen zu U .
  • target kann in einen Lvalue des Typs V konvertiert werden, sodass T referenzkompatibel mit V ist.

Dann bindet die Referenz an das Lvalue-Ergebnis der Konvertierung oder an dessen entsprechendes Basisklassen-Subobjekt:

struct A {};
struct B : A { operator int&(); };
int& ir = B(); // ir referenziert das Ergebnis von B::operator int&

Andernfalls, wenn die zu initialisierende Referenz eine Lvalue-Referenz ist und T nicht const-qualifiziert oder volatile-qualifiziert ist, ist das Programm fehlerhaft:

double& rd2 = 2.0; // Fehler: kein L-Wert und Referenz ist nicht const
int i = 2;
double& rd3 = i;   // Fehler: Typkonflikt und Referenz ist nicht const

Andernfalls, wenn alle folgenden Bedingungen erfüllt sind:

  • target ist ein Wert einer der folgenden Kategorien:
  • rvalue
(bis C++11)
  • non-bit-field xvalue
  • class prvalue
  • array prvalue
  • function lvalue
(seit C++11)
(bis C++17)
  • non-bit-field rvalue
  • function lvalue
(seit C++17)
  • T ist referenzkompatibel mit U .

Dann bindet die Referenz an target , oder an deren entsprechendes Basisklassen-Subobjekt:

struct A {};
struct B : A {};
extern B f();
const A& rca2 = f(); // gebunden an das A-Subobjekt des B-Rvalues.
A&& rra = f();       // dasselbe wie oben
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // direkt an i2 gebunden

Wenn target ein Prvalue ist, wird darauf temporäre Materialisierung angewendet, wobei der Typ des Prvalue als der angepasste Typ P betrachtet wird.

  • P wird vom Typ von target (d.h. U ) angepasst , indem die CV-Qualifikation von T hinzugefügt wird.

In diesem Fall bindet die Referenz an das Ergebnisobjekt oder an dessen entsprechende Basisklassen-Subobjekt.

(since C++17)

Andernfalls, wenn alle folgenden Bedingungen erfüllt sind:

  • U ist ein Klassentyp.
  • T ist nicht referenzbezogen zu U .
  • target kann in einen Wert v vom Typ V konvertiert werden, sodass T referenzkompatibel mit V ist, wobei v einer der folgenden Kategorien entspricht:
  • rvalue
(bis C++11)
  • xvalue
  • class prvalue
  • function lvalue
(seit C++11)
(bis C++17)
  • rvalue
  • function lvalue
(seit C++17)

Dann bindet die Referenz an das Ergebnis der Konvertierung oder an ihr entsprechendes Basisklassen-Subobjekt:

struct A {};
struct B : A {};
struct X { operator B(); } x;
const A& r = x; // gebunden an das A-Subobjekt des Ergebnisses der Konvertierung
B&& rrb = x;    // direkt an das Ergebnis der Konvertierung gebunden

Wenn das Ergebnis der Konvertierung ein Prvalue ist, Temporary Materialization wird darauf angewendet, wobei der Typ des Prvalue als der angepasste Typ P betrachtet wird.

  • P wird vom Typ des Konvertierungsergebnisses durch Hinzufügen der CV-Qualifikation von T angepasst .

In diesem Fall bindet die Referenz an das Ergebnisobjekt oder an dessen entsprechende Basisklassen-Subobjekt.

(seit C++17)

Indirekte Bindung

Wenn keine direkte Bindung verfügbar ist, wird eine indirekte Bindung in Betracht gezogen. In diesem Fall kann T nicht referenzbezogen zu U sein.

Wenn T oder U ein Klassentyp ist, werden benutzerdefinierte Konvertierungen gemäß den Regeln für die Kopierinitialisierung eines Objekts vom Typ T durch benutzerdefinierte Konvertierung berücksichtigt. Das Programm ist fehlerhaft, wenn die entsprechende Nicht-Referenz-Kopierinitialisierung fehlerhaft wäre. Das Ergebnis des Aufrufs der Konvertierungsfunktion, wie für die Nicht-Referenz- Kopierinitialisierung beschrieben, wird dann zur Direktinitialisierung der Referenz verwendet. Für diese Direktinitialisierung werden benutzerdefinierte Konvertierungen nicht berücksichtigt.

Andernfalls wird ein Temporär vom Typ T erstellt und von target kopierinitialisiert. Die Referenz wird dann an das Temporär gebunden.

(until C++17)

Andernfalls wird target implizit in einen Prvalue des Typs "cv-unqualified T " konvertiert. Die Temporärmaterialisierungskonvertierung wird angewendet, wobei der Typ des Prvalue als T betrachtet wird, und die Referenz wird an das Ergebnisobjekt gebunden.

(since C++17)
const std::string& rs = "abc"; // rs referenziert eine temporäre Kopie, die vom char-Array kopierinitialisiert wurde
const double& rcd2 = 2;        // rcd2 referenziert einen temporären Wert mit dem Wert 2.0
int i3 = 2;
double&& rrd3 = i3;            // rrd3 referenziert einen temporären Wert mit dem Wert 2.0

Lebensdauer eines Temporären

Immer wenn eine Referenz an ein temporäres Objekt oder an ein Unterobjekt davon gebunden wird, wird die Lebensdauer des temporären Objekts erweitert, um der Lebensdauer der Referenz zu entsprechen (siehe Ausnahmen bei der Lebensdauer temporärer Objekte ), wobei das temporäre Objekt oder sein Unterobjekt durch einen der folgenden Ausdrücke bezeichnet wird:

(bis C++17)
(seit C++17)

Es gelten folgende Ausnahmen von dieser Lebensdauerregel:

  • Ein temporäres Objekt, das an einen Rückgabewert einer Funktion in einer return -Anweisung gebunden ist, wird nicht verlängert: Es wird sofort am Ende des Rückgabeausdrucks zerstört. Eine solche return -Anweisung gibt immer eine hängende Referenz zurück.
(bis C++26)
  • Ein temporäres Objekt, das an einen Referenzparameter in einem Funktionsaufruf gebunden ist, existiert bis zum Ende des vollständigen Ausdrucks, der diesen Funktionsaufruf enthält: Wenn die Funktion eine Referenz zurückgibt, die über den vollständigen Ausdruck hinaus bestehen bleibt, wird sie zu einer hängenden Referenz.
  • Ein temporäres Objekt, das an eine Referenz im Initialisierer eines new-Ausdrucks gebunden ist, existiert bis zum Ende des vollständigen Ausdrucks, der diesen new-Ausdruck enthält, und nicht so lange wie das initialisierte Objekt. Wenn das initialisierte Objekt länger existiert als der vollständige Ausdruck, wird sein Referenzmitglied zu einer hängenden Referenz.
(since C++11)
  • Ein temporäres Objekt, das an eine Referenz in einem Referenzelement eines Aggregats gebunden ist, das mit Direktinitialisierung -Syntax ( runde Klammern ) initialisiert wird, existiert bis zum Ende des vollständigen Ausdrucks, der den Initialisierer enthält, im Gegensatz zur Listeninitialisierung -Syntax { geschweifte Klammern } .
struct A
{
    int&& r;
};
A a1{7}; // OK, Lebensdauer wird erweitert
A a2(7); // wohlgeformt, aber hängende Referenz
(seit C++20)

Im Allgemeinen kann die Lebensdauer eines temporären Objekts nicht weiter verlängert werden, indem "es weitergegeben wird": Ein zweiter Verweis, der von der Referenzvariablen oder dem Datenelement initialisiert wird, an das das temporäre Objekt gebunden war, beeinflusst dessen Lebensdauer nicht.

Hinweise

Referenzen erscheinen ohne Initialisierer nur in Funktionsparameterdeklarationen, in Funktionsrückgabetypdeklarationen, in der Deklaration eines Klassenmembers und mit dem extern Spezifizierer.

Bis zur Lösung von CWG issue 1696 war es erlaubt, dass ein temporäres Objekt an ein Referenzmitglied in einer Initialisiererliste eines Konstruktors gebunden wird, und es bestand nur bis zum Verlassen des Konstruktors, nicht so lange wie das Objekt existiert. Eine solche Initialisierung ist seit CWG 1696 fehlerhaft, obwohl viele Compiler sie weiterhin unterstützen (eine bemerkenswerte Ausnahme ist clang).

Beispiel

#include <sstream>
#include <utility>
struct S
{
    int mi;
    const std::pair<int, int>& mp; // Referenz-Member
};
void foo(int) {}
struct A {};
struct B : A
{
    int n;
    operator int&() { return n; }
};
B bar() { return B(); }
//int& bad_r;      // Fehler: kein Initialisierer
extern int& ext_r; // OK
int main()
{
//  Lvalues
    int n = 1;
    int& r1 = n;                    // Lvalue-Referenz auf das Objekt n
    const int& cr(n);               // Referenz kann mehr cv-qualifiziert sein
    volatile int& cv{n};            // Jede Initialisierungssyntax kann verwendet werden
    int& r2 = r1;                   // Weitere Lvalue-Referenz auf das Objekt n
//  int& bad = cr;                  // Fehler: weniger cv-qualifiziert
    int& r3 = const_cast<int&>(cr); // const_cast ist erforderlich
    void (&rf)(int) = foo; // Lvalue-Referenz auf Funktion
    int ar[3];
    int (&ra)[3] = ar;     // Lvalue-Referenz auf Array
    B b;
    A& base_ref = b;        // Referenz auf Basis-Subobjekt
    int& converted_ref = b; // Referenz auf das Ergebnis einer Konvertierung
//  Rvalues
//  int& bad = 1;        // Fehler: Lvalue-Ref kann nicht an Rvalue gebunden werden
    const int& cref = 1; // an Rvalue gebunden
    int&& rref = 1;      // an Rvalue gebunden
    const A& cref2 = bar(); // Referenz auf A-Subobjekt von B-Temporär
    A&& rref2 = bar();      // dasselbe
    int&& xref = static_cast<int&&>(n); // direkt an n binden
//  int&& copy_ref = n;                 // Fehler: kann nicht an Lvalue gebunden werden
    double&& copy_ref = n;              // an Rvalue-Temporär mit Wert 1.0 binden
//  Einschränkungen bei Temporär-Lebensdauern
//  std::ostream& buf_ref = std::ostringstream() << 'a';
                     // das ostringstream-Temporär wurde an den linken Operanden
                     // von operator<< gebunden, aber seine Lebensdauer endete beim Semikolon,
                     // daher ist buf_ref eine hängende Referenz
    S a {1, {2, 3}}; // Temporäres Paar {2, 3} an das Referenz-Member
                     // a.mp gebunden und seine Lebensdauer wird erweitert, um
                     // der Lebensdauer von Objekt a zu entsprechen
    S* p = new S{1, {2, 3}}; // Temporäres Paar {2, 3} an das Referenz-
                             // Member p->mp gebunden, aber seine Lebensdauer endete beim Semikolon
                             // p->mp ist eine hängende Referenz
    delete p;
    // Imitiere [[maybe_unused]] angewendet auf folgende Variablen:
    [](...){}
    (
        cv, r2, r3, rf, ra, base_ref, converted_ref,
        a, cref, rref, cref2, rref2, copy_ref, xref
    );
}

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 391 C++98 Initialisierung einer Referenz auf const-qualifizierten Typ mit einem Klassen-Rvalue
könnte ein Temporär erzeugen, und ein Konstruktor dieser Klasse
war erforderlich, um den Rvalue in dieses Temporär zu kopieren
Es wird kein Temporär erzeugt,
Konstruktor ist nicht
erforderlich
CWG 450 C++98 Eine Referenz auf const-qualifiziertes Array konnte nicht
mit einem referenzkompatiblen Array-Rvalue initialisiert werden
Erlaubt
CWG 589 C++98 Eine Referenz konnte nicht direkt an einen Array- oder Klassen-Rvalue binden Erlaubt
CWG 656 C++98 Eine Referenz auf const-qualifizierten Typ, initialisiert mit einem nicht
referenzkompatiblen Typ, der aber eine Konvertierungsfunktion zu einem referenz-
kompatiblen Typ besitzt, wurde an ein Temporär gebunden, das vom Rückgabe-
wert (oder seinem Basisklassen-Subobjekt) der Konvertierungsfunktion kopiert wurde
Direkt an den Rückgabewert
(oder sein Basisklassen-
Subobjekt) gebunden
CWG 1287 C++11 Die Konvertierung vom target vom Klassentyp zu einem anderen
referenzkompatiblen Typ konnte nur implizit erfolgen
Explizite Konvertierungen
erlaubt
CWG 1295 C++11 Eine Referenz konnte an ein Bitfeld-XValue binden Verboten
CWG 1299 C++98 Die Definition von Temporär war unklar Klargestellt
CWG 1571 C++98 Benutzerdefinierte Konvertierungen bei indirekter
Bindung berücksichtigten nicht den Typ von target
Berücksichtigt
CWG 1604 C++98 Benutzerdefinierte Konvertierungen wurden bei indirekter Bindung nicht berücksichtigt Berücksichtigt
CWG 2352 C++98 Referenzkompatibilität berücksichtigte keine Qualifikationskonvertierungen Berücksichtigt
CWG 2481 C++17 CV-Qualifikation wurde nicht zum Ergebnistyp
der Temporärmaterialisierung bei indirekter Bindung hinzugefügt
Hinzugefügt
CWG 2657 C++17 CV-Qualifikation wurde nicht zum Ergebnistyp
der Temporärmaterialisierung bei direkter Bindung hinzugefügt
Hinzugefügt
CWG 2801 C++98 Referenz-verwandte Typen waren für indirekte Bindung erlaubt Verboten

Siehe auch