Namensräume
Varianten
Aktionen

Referenzdeklaration

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
 
 

Deklariert eine benannte Variable als Referenz, d.h. als Alias für ein bereits vorhandenes Objekt oder eine Funktion.

Inhalt

[bearbeiten] Syntax

Eine Referenzvariablendeklaration ist jede einfache Deklaration, deren Deklarator die Form hat

& attr (optional) Deklarator (1)
&& attr (optional) Deklarator (2) (seit C++11)
1) Lvalue-Referenz-Deklarator: Die Deklaration S& D; deklariert D als *lvalue-Referenz* auf den Typ, der durch decl-specifier-seq S bestimmt wird.
2) Rvalue-Referenz-Deklarator: Die Deklaration S&& D; deklariert D als *rvalue-Referenz* auf den Typ, der durch decl-specifier-seq S bestimmt wird.
Deklarator - jeder Deklarator außer einem anderen Referenzdeklarator (es gibt keine Referenzen auf Referenzen)
attr - (seit C++11) Liste von Attributen

Eine Referenz muss initialisiert werden, um auf ein gültiges Objekt oder eine Funktion zu verweisen: siehe Referenzinitialisierung.

Der Typ „Referenz auf (möglicherweise cv-qualifiziertes) void“ kann nicht gebildet werden.

Referenztypen können auf der obersten Ebene nicht cv-qualifiziert sein; es gibt keine Syntax dafür in der Deklaration, und wenn eine Qualifikation zu einem Typedef-Namen oder einem decltype-Speziifizierer(seit C++11) oder einem Typ-Template-Parameter hinzugefügt wird, wird sie ignoriert.

Referenzen sind keine Objekte; sie belegen nicht notwendigerweise Speicherplatz, obwohl der Compiler Speicherplatz zuweisen kann, wenn dies zur Implementierung der gewünschten Semantik erforderlich ist (z. B. erhöht ein nicht-statisches Datenelement vom Referenztyp normalerweise die Größe der Klasse um den für die Speicherung einer Speicheradresse erforderlichen Betrag).

Da Referenzen keine Objekte sind, gibt es keine Arrays von Referenzen, keine Zeiger auf Referenzen und keine Referenzen auf Referenzen.

int& a[3]; // error
int&* p;   // error
int& &r;   // error

Referenzkollaps

Es ist zulässig, Referenzen auf Referenzen durch Typmanipulationen in Templates oder Typedefs zu bilden, in welchen Fall die Regeln des *Referenzkollapses* gelten: Rvalue-Referenz auf Rvalue-Referenz kollabiert zu Rvalue-Referenz, alle anderen Kombinationen bilden Lvalue-Referenz.

typedef int&  lref;
typedef int&& rref;
int n;
 
lref&  r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&
rref&  r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&

(Dies bildet zusammen mit speziellen Regeln für die Template-Argument-Deduktion, wenn T&& in einem Funktionstemplate verwendet wird, die Regeln, die std::forward ermöglichen.)

(seit C++11)

[bearbeiten] Lvalue-Referenzen

Lvalue-Referenzen können verwendet werden, um bestehende Objekte zu aliasen (optional mit unterschiedlicher cv-Qualifikation).

#include <iostream>
#include <string>
 
int main()
{
    std::string s = "Ex";
    std::string& r1 = s;
    const std::string& r2 = s;
 
    r1 += "ample";           // modifies s
//  r2 += "!";               // error: cannot modify through reference to const
    std::cout << r2 << '\n'; // prints s, which now holds "Example"
}

Sie können auch verwendet werden, um Pass-by-Reference-Semantik in Funktionsaufrufen zu implementieren.

#include <iostream>
#include <string>
 
void double_string(std::string& s)
{
    s += s; // 's' is the same object as main()'s 'str'
}
 
int main()
{
    std::string str = "Test";
    double_string(str);
    std::cout << str << '\n';
}

Wenn der Rückgabetyp einer Funktion eine Lvalue-Referenz ist, wird der Funktionsaufrufausdruck zu einem Lvalue-Ausdruck.

#include <iostream>
#include <string>
 
char& char_number(std::string& s, std::size_t n)
{
    return s.at(n); // string::at() returns a reference to char
}
 
int main()
{
    std::string str = "Test";
    char_number(str, 1) = 'a'; // the function call is lvalue, can be assigned to
    std::cout << str << '\n';
}

Rvalue-Referenzen

Rvalue-Referenzen können verwendet werden, um die Lebensdauer von temporären Objekten zu verlängern (beachten Sie, dass const-Lvalue-Referenzen auch die Lebensdauer von temporären Objekten verlängern können, aber sie sind nicht modifizierbar über sie).

#include <iostream>
#include <string>
 
int main()
{
    std::string s1 = "Test";
//  std::string&& r1 = s1;           // error: can't bind to lvalue
 
    const std::string& r2 = s1 + s1; // okay: lvalue reference to const extends lifetime
//  r2 += "Test";                    // error: can't modify through reference to const
 
    std::string&& r3 = s1 + s1;      // okay: rvalue reference extends lifetime
    r3 += "Test";                    // okay: can modify through reference to non-const
    std::cout << r3 << '\n';
}

Wichtiger ist, dass, wenn eine Funktion sowohl Rvalue-Referenz- als auch Lvalue-Referenz-Überladungen hat, die Rvalue-Referenz-Überladung an Rvalues bindet (einschließlich sowohl prvalues als auch xvalues), während die Lvalue-Referenz-Überladung an Lvalues bindet.

#include <iostream>
#include <utility>
 
void f(int& x)
{
    std::cout << "lvalue reference overload f(" << x << ")\n";
}
 
void f(const int& x)
{
    std::cout << "lvalue reference to const overload f(" << x << ")\n";
}
 
void f(int&& x)
{
    std::cout << "rvalue reference overload f(" << x << ")\n";
}
 
int main()
{
    int i = 1;
    const int ci = 2;
 
    f(i);  // calls f(int&)
    f(ci); // calls f(const int&)
    f(3);  // calls f(int&&)
           // would call f(const int&) if f(int&&) overload wasn't provided
    f(std::move(i)); // calls f(int&&)
 
    // rvalue reference variables are lvalues when used in expressions
    int&& x = 1;
    f(x);            // calls f(int& x)
    f(std::move(x)); // calls f(int&& x)
}

Dies ermöglicht, dass Move-Konstruktoren, Move-Zuweisungsoperatoren und andere Move-fähige Funktionen (z. B. std::vector::push_back()) automatisch ausgewählt werden, wenn sie geeignet sind.

Da Rvalue-Referenzen an xvalues binden können, können sie sich auf Nicht-temporäre Objekte beziehen.

int i2 = 42;
int&& rri = std::move(i2); // binds directly to i2

Dies ermöglicht das Verschieben aus einem Objekt im Gültigkeitsbereich, das nicht mehr benötigt wird.

std::vector<int> v{1, 2, 3, 4, 5};
std::vector<int> v2(std::move(v)); // binds an rvalue reference to v
assert(v.empty());

Weiterleitungsreferenzen

Weiterleitungsreferenzen sind eine spezielle Art von Referenzen, die die Wertkategorie eines Funktionsarguments beibehalten und es ermöglichen, es mittels std::forward zu *weiterleiten*. Weiterleitungsreferenzen sind entweder

1) ein Funktionsparameter eines Funktionstemplate, deklariert als Rvalue-Referenz auf einen cv-unqualifizierten Typ-Template-Parameter desselben Funktionstemplate
template<class T>
int f(T&& x)                      // x is a forwarding reference
{
    return g(std::forward<T>(x)); // and so can be forwarded
}
 
int main()
{
    int i;
    f(i); // argument is lvalue, calls f<int&>(int&), std::forward<int&>(x) is lvalue
    f(0); // argument is rvalue, calls f<int>(int&&), std::forward<int>(x) is rvalue
}
 
template<class T>
int g(const T&& x); // x is not a forwarding reference: const T is not cv-unqualified
 
template<class T>
struct A
{
    template<class U>
    A(T&& x, U&& y, int* p); // x is not a forwarding reference: T is not a
                             // type template parameter of the constructor,
                             // but y is a forwarding reference
};
2) auto&&, außer wenn es von einer in geschweifte Klammern eingeschlossenen Initialisierungsliste abgeleitet wird oder, wenn es einen Template-Parameter eines Klassentemplates während der Argumentdeduktion für Klassentemplates repräsentiert(seit C++17)
auto&& vec = foo();       // foo() may be lvalue or rvalue, vec is a forwarding reference
auto i = std::begin(vec); // works either way
(*i)++;                   // works either way
 
g(std::forward<decltype(vec)>(vec)); // forwards, preserving value category
 
for (auto&& x: f())
{
    // x is a forwarding reference; this is a common way to use range for in generic code
}
 
auto&& z = {1, 2, 3}; // *not* a forwarding reference (special case for initializer lists)

Siehe auch Template-Argument-Deduktion und std::forward.

(seit C++11)

[bearbeiten] Hängende Referenzen

Obwohl Referenzen bei der Initialisierung immer auf gültige Objekte oder Funktionen verweisen, ist es möglich, ein Programm zu erstellen, bei dem die Lebensdauer des referenzierten Objekts endet, die Referenz aber zugänglich bleibt (d.h. *hängend* wird).

Gegeben sei ein Ausdruck expr vom Referenztyp und sei target das von der Referenz bezeichnete Objekt oder die Funktion.

  • Wenn ein Zeiger auf target im Kontext der Auswertung von expr gültig wäre, bezeichnet das Ergebnis target.
  • Andernfalls ist das Verhalten undefiniert.
std::string& f()
{
    std::string s = "Example";
    return s; // exits the scope of s:
              // its destructor is called and its storage deallocated
}
 
std::string& r = f(); // dangling reference
std::cout << r;       // undefined behavior: reads from a dangling reference
std::string s = f();  // undefined behavior: copy-initializes from a dangling reference

Beachten Sie, dass Rvalue-Referenzen und const-Lvalue-Referenzen die Lebensdauer von temporären Objekten verlängern (siehe Referenzinitialisierung für Regeln und Ausnahmen).

Wenn das referenzierte Objekt zerstört wurde (z. B. durch expliziten Destruktoraufruf), aber der Speicher nicht freigegeben wurde, kann eine Referenz auf das Objekt außerhalb der Lebensdauer in begrenzten Umfang verwendet werden und gültig werden, wenn das Objekt im selben Speicher neu erstellt wird (siehe Zugriff außerhalb der Lebensdauer für Details).

[bearbeiten] Typ-unzugängliche Referenzen

Der Versuch, eine Referenz an ein Objekt zu binden, wobei der konvertierte Initialisierer ein Lvalue(bis C++11)ein glvalue(seit C++11) ist, über den das Objekt nicht typ-zugänglich ist, führt zu undefiniertem Verhalten.

char x alignas(int);
 
int& ir = *reinterpret_cast<int*>(&x); // undefined behavior:
                                       // initializer refers to char object

[bearbeiten] Aufrufinkompatible Referenzen

Der Versuch, eine Referenz an eine Funktion zu binden, wobei der konvertierte Initialisierer ein Lvalue(bis C++11)ein glvalue(seit C++11) ist, dessen Typ nicht aufrufkompatibel mit dem Typ der Funktionsdefinition ist, führt zu undefiniertem Verhalten.

void f(int);
 
using F = void(float);
F& ir = *reinterpret_cast<F*>(&f); // undefined behavior:
                                   // initializer refers to void(int) function

[bearbeiten] Hinweise

Feature-Testmakro Wert Std Feature
__cpp_rvalue_references 200610L (C++11) Rvalue-Referenzen

[bearbeiten] 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 453 C++98 Es war unklar, an welches Objekt oder welche Funktion eine Referenz nicht gebunden werden kann. wurde klargestellt
CWG 1510 C++11 cv-qualifizierte Referenzen konnten im Operanden von decltype nicht gebildet werden. erlaubt
CWG 2550 C++98 Parameter konnten den Typ „Referenz auf void“ haben. disallowed
CWG 2933 C++98 Das Verhalten beim Zugriff auf hängende Referenzen war unklar. wurde klargestellt

[bearbeiten] Externe Links

Thomas Becker, 2013 - C++ Rvalue References Explained