Namensräume
Varianten
Aktionen

Referenzinitialisierung

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Bindet eine Referenz an ein Objekt.

Inhalt

[bearbeiten] Syntax

[bearbeiten] 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 )
[bearbeiten] Gewöhnliche Listen-Initialisierung (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)
[bearbeiten] Benannte Listen-Initialisierung (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)

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 zugewiesen werden (geändert werden), um auf ein anderes Objekt zu verweisen.

Referenzen werden in den folgenden Situationen initialisiert

1) Wenn eine benannte Lwert-Referenz-Variable mit einem Initialisierer deklariert wird.
2) Wenn eine benannte Rwert-Referenz-Variable mit einem Initialisierer deklariert wird.
3) In einem Funktionsaufruf-Ausdruck, wenn der Funktionsparameter einen Referenztyp hat.
4) In der return-Anweisung, wenn die Funktion einen Referenztyp zurückgibt. Das Programm ist ill-formed, wenn die zurückgegebene Referenz an das Ergebnis eines Temporär objekts gebunden wird.(seit C++26)
5) Wenn ein Nicht-Statische-Datenmember vom Referenztyp mit einem Member-Initialisierer initialisiert wird.

[bearbeiten] Erklärung

T - der Referenztyp
ref - die zu initialisierende Referenzvariable
target - der verwendete Initialisiererausdruck
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))
Klasse - ein Klassename
ref-member - ein Nicht-Statische-Datenmember vom Referenztyp (T & oder T &&(seit C++11)) von Class
des1, des2, ... - Deskriptoren
arg1, arg2, ... - die Initialisierer in Initialisierungslisten

[bearbeiten] 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" in den Typ "Zeiger auf T1" über eine Standardkonvertierungssequenz konvertiert werden kann, dann ist T1 *referenzkompatibel* mit T2.

[bearbeiten] Initialisierungsregeln

Wenn eine Referenzinitialisierung eine gewöhnliche oder benannte(seit C++20) Listen-Initialisierung verwendet, werden die Regeln der Listen-Initialisierung befolgt.

(seit C++11)

Für Nicht-Listen-Referenzinitialisierung, gegeben den Typ von target als U, bindet sich die Referenz entweder *direkt* an target oder an einen Wert vom Typ T, der von target konvertiert wurde. Direkte Bindung wird zuerst betrachtet, gefolgt von indirekter Bindung. Wenn keine Bindung verfügbar ist, ist das Programm ill-formed.

In allen Fällen, in denen die Referenzkompatibilitätsbeziehung zweier Typen verwendet wird, um die Gültigkeit einer Referenzbindung herzustellen, und die Standardkonvertierungssequenz ill-formed wäre, ist ein Programm, das eine solche Bindung erfordert, ill-formed.

[bearbeiten] Direkte Bindung

Wenn alle folgenden Bedingungen erfüllt sind

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

Dann bindet sich die Referenz an target oder an dessen entsprechenden Basisklassen-Subobjekt.

double d = 2.0;
double& rd = d;        // rd refers to d
const double& rcd = d; // rcd refers to d
 
struct A {};
struct B : A {} b;
 
A& ra = b;             // ra refers to A subobject in b
const A& rca = b;      // rca refers to A subobject in b

Andernfalls, wenn alle folgenden Bedingungen erfüllt sind

  • Die zu initialisierende Referenz ist eine Lwert-Referenz.
  • U ist ein Klassentyp.
  • T ist nicht referenzbezogen zu U.
  • target kann in einen Lwert vom Typ V konvertiert werden, so dass T referenzkompatibel mit V ist.

Dann bindet sich die Referenz an den Lwert-Ergebnis der Konvertierung oder an dessen entsprechenden Basisklassen-Subobjekt.

struct A {};
struct B : A { operator int&(); };
 
int& ir = B(); // ir refers to the result of B::operator int&

Andernfalls, wenn die zu initialisierende Referenz eine Lwert-Referenz ist und T nicht const-qualifiziert oder volatile-qualifiziert ist, ist das Programm ill-formed.

double& rd2 = 2.0; // error: not an lvalue and reference is not const
int i = 2;
double& rd3 = i;   // error: type mismatch and reference is not const

Andernfalls, wenn alle folgenden Bedingungen erfüllt sind

  • target ist ein Wert einer der folgenden Kategorien
  • rvalue
(bis C++11)
  • Nicht-Bitfeld-xvalue
  • Klassen-prvalue
  • Array-prvalue
  • Funktions-Lwert
(seit C++11)
(bis C++17)
  • Nicht-Bitfeld-rvalue
  • Funktions-Lwert
(seit C++17)
  • T ist referenzkompatibel mit U.

Dann bindet sich die Referenz an target oder an dessen entsprechenden Basisklassen-Subobjekt.

struct A {};
struct B : A {};
extern B f();
 
const A& rca2 = f(); // bound to the A subobject of the B rvalue.
A&& rra = f();       // same as above
 
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // bound directly to i2

Wenn target ein prvalue ist, wird Temporär-Materialisierung darauf angewendet, wobei der Typ des prvalue als der angepasste Typ P betrachtet wird.

  • P wird von dem Typ von target (d.h. U) durch Hinzufügen der cv-Qualifikation von T angepasst.

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

(seit 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, so dass T referenzkompatibel mit V ist, wobei v eine der folgenden Kategorien hat
  • rvalue
(bis C++11)
  • xvalue
  • Klassen-prvalue
  • Funktions-Lwert
(seit C++11)
(bis C++17)
  • rvalue
  • Funktions-Lwert
(seit C++17)

Dann bindet sich die Referenz an das Ergebnis der Konvertierung oder an dessen entsprechenden Basisklassen-Subobjekt.

struct A {};
struct B : A {};
struct X { operator B(); } x;
 
const A& r = x; // bound to the A subobject of the result of the conversion
B&& rrb = x;    // bound directly to the result of the conversion

Wenn das Ergebnis der Konvertierung ein prvalue ist, wird Temporär-Materialisierung 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 sich die Referenz an das Ergebnisobjekt oder an dessen entsprechenden Basisklassen-Subobjekt.

(seit C++17)

[bearbeiten] Indirekte Bindung

Wenn direkte Bindung nicht verfügbar ist, wird indirekte Bindung betrachtet. In diesem Fall ist T nicht referenzbezogen zu U.

Wenn T oder U ein Klassentyp ist, werden benutzerdefinierte Konvertierungen unter Verwendung der Regeln für die Kopierinitialisierung eines Objekts vom Typ T durch benutzerdefinierte Konvertierung berücksichtigt. Das Programm ist ill-formed, wenn die entsprechende Nicht-Referenz-Kopierinitialisierung ill-formed wäre. Das Ergebnis des Aufrufs der Konvertierungsfunktion, wie für die Nicht-Referenz Kopierinitialisierung beschrieben, wird dann zur direkten Initialisierung der Referenz verwendet. Für diese direkte Initialisierung werden keine benutzerdefinierten Konvertierungen berücksichtigt.

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

(bis C++17)

Andernfalls wird target implizit in einen prvalue vom Typ "cv-unqualifiziertes T" konvertiert. Die Temporär-Materialisierungs-Konvertierung wird angewendet, wobei der Typ des prvalue als T betrachtet wird, und die Referenz wird an das Ergebnisobjekt gebunden.

(seit C++17)
const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array
const double& rcd2 = 2;        // rcd2 refers to temporary with value 2.0
int i3 = 2;
double&& rrd3 = i3;            // rrd3 refers to temporary with value 2.0

[bearbeiten] Lebensdauer eines Temporär objekts

Immer wenn eine Referenz an ein Temporär objekt oder ein Subobjekt davon gebunden wird, wird die Lebensdauer des Temporär objekts so verlängert, dass sie mit der Lebensdauer der Referenz übereinstimmt (siehe Ausnahmen für die Lebensdauer von Temporär objekten), wobei das Temporär objekt oder sein Subobjekt durch einen der folgenden Ausdrücke bezeichnet wird

  • ein prvalue-Ausdruck eines Objekttyps,
(bis C++17)
(seit C++17)
  • ein in Klammern gesetzter Ausdruck (e), wobei e einer dieser Ausdrücke ist,
  • ein eingebauter Subskript-Ausdruck der Form a[n] oder n[a], wobei a ein Array ist und einer dieser Ausdrücke ist,
  • ein Klassen-Member-Zugriffs-Ausdruck der Form e.m, wobei e einer dieser Ausdrücke ist und m ein Nicht-Statische-Datenmember eines Objekttyps bezeichnet,
  • eine Zeiger-auf-Member-Operation der Form e.*mp, wobei e einer dieser Ausdrücke ist und mp ein Zeiger auf ein Datenmember ist,
  • eine const_cast, static_cast, dynamic_cast oder reinterpret_cast-Konvertierung ohne benutzerdefinierte Konvertierung, die einen dieser Ausdrücke in einen Lwert umwandelt, der auf das vom Operanden bezeichnete Objekt oder dessen vollständiges Objekt oder ein Subobjekt davon verweist (ein expliziter Cast-Ausdruck wird als Sequenz dieser Casts interpretiert),
  • ein bedingter Ausdruck der Form cond ? e1 : e2, der ein Lwert ist, wobei e1 oder e2 einer dieser Ausdrücke ist, oder
  • ein eingebauter Komma-Ausdruck der Form x, e, der ein Lwert ist, wobei e einer dieser Ausdrücke ist.

Es gibt folgende Ausnahmen zu dieser Lebensdauerregel

  • ein Temporär objekt, das an den 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är 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 länger lebt als der vollständige Ausdruck, wird sie zu einer hängenden Referenz.
  • ein Temporär 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, nicht so lange wie das initialisierte Objekt. Wenn das initialisierte Objekt den vollständigen Ausdruck überlebt, wird sein Referenzmember zu einer hängenden Referenz.
(seit C++11)
  • ein Temporär objekt, das an eine Referenz in einem Referenzelement eines Aggregats gebunden ist, das mit der Direktinitialisierungs-Syntax **(**Klammern**)** initialisiert wird, existiert bis zum Ende des vollständigen Ausdrucks, der den Initialisierer enthält, im Gegensatz zur Listeninitialisierungs-Syntax **{**geschweifte Klammern**}**.
struct A
{
    int&& r;
};
 
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference
(seit C++20)

Im Allgemeinen kann die Lebensdauer eines Temporär objekts nicht weiter verlängert werden, indem es "weitergegeben" wird: eine zweite Referenz, die von der Referenzvariable oder dem Datenmember, an den das Temporär objekt gebunden war, initialisiert wird, beeinflusst dessen Lebensdauer nicht.

[bearbeiten] Anmerkungen

Referenzen ohne Initialisierer treten nur in der Deklaration von Funktionsparametern, in der Deklaration von Funktionsrückgabetypen, in der Deklaration eines Klassenmembers und mit dem extern-Speziplikator auf.

Bis zur Auflösung von CWG Issue 1696 war es gestattet, ein Temporär objekt an ein Referenzmember in der Initialisierungsliste eines Konstruktors zu binden, und es existierte nur bis zum Ende des Konstruktors, nicht so lange wie das Objekt existiert. Eine solche Initialisierung ist seit CWG 1696 ill-formed, obwohl viele Compiler sie immer noch unterstützen (eine bemerkenswerte Ausnahme ist clang).

[bearbeiten] Beispiel

#include <sstream>
#include <utility>
 
struct S
{
    int mi;
    const std::pair<int, int>& mp; // reference member
};
 
void foo(int) {}
 
struct A {};
 
struct B : A
{
    int n;
    operator int&() { return n; }
};
 
B bar() { return B(); }
 
//int& bad_r;      // error: no initializer
extern int& ext_r; // OK
 
int main()
{
//  Lvalues
    int n = 1;
    int& r1 = n;                    // lvalue reference to the object n
    const int& cr(n);               // reference can be more cv-qualified
    volatile int& cv{n};            // any initializer syntax can be used
    int& r2 = r1;                   // another lvalue reference to the object n
//  int& bad = cr;                  // error: less cv-qualified
    int& r3 = const_cast<int&>(cr); // const_cast is needed
 
    void (&rf)(int) = foo; // lvalue reference to function
    int ar[3];
    int (&ra)[3] = ar;     // lvalue reference to array
 
    B b;
    A& base_ref = b;        // reference to base subobject
    int& converted_ref = b; // reference to the result of a conversion
 
//  Rvalues
//  int& bad = 1;        // error: cannot bind lvalue ref to rvalue
    const int& cref = 1; // bound to rvalue
    int&& rref = 1;      // bound to rvalue
 
    const A& cref2 = bar(); // reference to A subobject of B temporary
    A&& rref2 = bar();      // same
 
    int&& xref = static_cast<int&&>(n); // bind directly to n
//  int&& copy_ref = n;                 // error: can't bind to an lvalue
    double&& copy_ref = n;              // bind to an rvalue temporary with value 1.0
 
//  Restrictions on temporary lifetimes
//  std::ostream& buf_ref = std::ostringstream() << 'a';
                     // the ostringstream temporary was bound to the left operand
                     // of operator<< but its lifetime ended at the semicolon so
                     // the buf_ref is a dangling reference
 
    S a {1, {2, 3}}; // temporary pair {2, 3} bound to the reference member
                     // a.mp and its lifetime is extended to match 
                     // the lifetime of object a
 
    S* p = new S{1, {2, 3}}; // temporary pair {2, 3} bound to the reference
                             // member p->mp, but its lifetime ended at the semicolon
                             // p->mp is a dangling reference
    delete p;
 
    // Imitate [[maybe_unused]] applied to the following variables:
    [](...){}
    (
        cv, r2, r3, rf, ra, base_ref, converted_ref,
        a, cref, rref, cref2, rref2, copy_ref, xref
    );
}

[bearbeiten] Defect reports

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 einen const-qualifizierten Typ mit einem Klassentyp
rvalue kann ein Temporär objekt erzeugen und ein Konstruktor dieser Klasse
war erforderlich, um den rvalue in dieses Temporär objekt zu kopieren
kein Temporär objekt wird
erzeugt, Konstruktor
wird nicht benötigt
CWG 450 C++98 Eine Referenz auf ein const-qualifiziertes Array konnte nicht
mit einem referenzkompatiblen Array-rvalue initialisiert werden
erlaubt
CWG 589 C++98 Eine Referenz konnte sich nicht direkt an ein Array- oder Klassen-rvalue binden erlaubt
CWG 656 C++98 Eine Referenz auf einen const-qualifizierten Typ, initialisiert mit einem Typ, der nicht
referenzkompatibel ist, aber eine Konvertierungsfunktion zu einem referenzkompatiblen
Typ hat, wurde an ein Temporär objekt gebunden, das vom Rückgabewert
(oder dessen Basisklassen-Subobjekt) der Konvertierungsfunktion kopiert wurde
wurde an den Rückgabe-
wert (oder dessen Basisklassen-
Subobjekt) direkt gebunden
CWG 1287 C++11 Die Konvertierung von target vom Klassentyp in einen anderen
referenzkompatiblen Typ konnte nur implizit erfolgen
erlaube explizite
Konvertierungen
CWG 1295 C++11 Eine Referenz konnte sich an ein Bitfeld-xvalue binden verboten
CWG 1299 C++98 Die Definition von Temporär objekt war unklar wurde 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 Die cv-Qualifikation wurde nicht zum Ergebnistyp
der Temporär-Materialisierung bei indirekter Bindung hinzugefügt
hinzugefügt
CWG 2657 C++17 Die cv-Qualifikation wurde nicht zum Ergebnistyp
der Temporär-Materialisierung bei direkter Bindung
hinzugefügt
CWG 2801 C++98 Referenzbezogene Typen waren für die indirekte Bindung zugelassen verboten

[bearbeiten] Siehe auch