Referenzdeklaration
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) | |||||||
D als *lvalue-Referenz* auf den Typ, der durch decl-specifier-seq S bestimmt wird.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
ReferenzkollapsEs 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 |
(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-ReferenzenRvalue-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). Führen Sie diesen Code aus #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. Führen Sie diesen Code aus #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()); WeiterleitungsreferenzenWeiterleitungsreferenzen 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 |