Reference declaration
Deklariert eine benannte Variable als Referenz, also einen Alias für ein bereits existierendes Objekt oder eine bereits existierende Funktion.
Inhaltsverzeichnis |
Syntax
Eine Referenzvariablendeklaration ist jede einfache Deklaration, deren Deklarator die Form hat
&
attr
(optional)
declarator
|
(1) | ||||||||
&&
attr
(optional)
declarator
|
(2) | (seit C++11) | |||||||
D
als eine
Lvalue-Referenz
auf den durch
decl-specifier-seq
S
bestimmten Typ.
D
als eine
Rvalue-Referenz
auf den durch
decl-specifier-seq
S
bestimmten Typ.
| declarator | - | jeder Deklarator außer Referenzdeklarator, Arraydeklarator und Zeigerdeklarator (es gibt keine Referenzen auf Referenzen, Arrays von Referenzen oder Zeiger auf Referenzen) |
| attr | - | (since C++11) Liste von Attributen |
Eine Referenz muss initialisiert werden, um auf ein gültiges Objekt oder eine Funktion zu verweisen: siehe reference initialization .
Der Typ "Referenz auf (möglicherweise cv-qualifiziertes) void " kann nicht gebildet werden.
Referenztypen können nicht auf oberster Ebene
cv-qualifiziert
werden; es gibt keine Syntax dafür in Deklarationen, und wenn eine Qualifizierung zu einem Typedef-Namen
oder einem
decltype
-Spezifizierer hinzugefügt wird,
(seit C++11)
oder einem
Typ-Template-Parameter
, wird sie ignoriert.
Referenzen sind keine Objekte; sie belegen nicht zwangsläufig Speicher, obwohl der Compiler Speicher allokieren kann, falls dies notwendig ist, um die gewünschte Semantik zu implementieren (z.B. erhöht ein nicht-statisches Datenelement vom Referenztyp üblicherweise die Größe der Klasse um den Betrag, der zur Speicherung einer Speicheradresse erforderlich ist).
Da Referenzen keine Objekte sind, gibt es keine Arrays von Referenzen, keine Zeiger auf Referenzen und keine Referenzen auf Referenzen:
int& a[3]; // Fehler int&* p; // Fehler int& &r; // Fehler
Reference CollapsingEs ist erlaubt, Referenzen auf Referenzen durch Typmanipulationen in Templates oder Typdefinitionen zu bilden, wobei die Reference-Collapsing -Regeln Anwendung finden: Eine Rvalue-Referenz auf eine Rvalue-Referenz kollabiert zu einer Rvalue-Referenz, alle anderen Kombinationen bilden eine 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-Argumentdeduktion
, wenn
|
(seit C++11) |
Lvalue-Referenzen
Lvalue-Referenzen können verwendet werden, um ein bestehendes Objekt 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"; // modifiziert s // r2 += "!"; // Fehler: Kann nicht über const-Referenz modifizieren std::cout << r2 << '\n'; // gibt s aus, das jetzt "Example" enthält }
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' ist dasselbe Objekt wie 'str' in main() } 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() gibt eine Referenz auf char zurück } int main() { std::string str = "Test"; char_number(str, 1) = 'a'; // Der Funktionsaufruf ist ein L-Wert, kann zugewiesen werden std::cout << str << '\n'; }
Rvalue-ReferenzenRvalue-Referenzen können verwendet werden, um die Lebensdauer temporärer Objekte zu verlängern (beachten Sie, dass Lvalue-Referenzen auf const ebenfalls die Lebensdauer temporärer Objekte verlängern können, jedoch können diese nicht durch sie modifiziert werden):
Diesen Code ausführen
#include <iostream> #include <string> int main() { std::string s1 = "Test"; // std::string&& r1 = s1; // Fehler: kann nicht an Lvalue gebunden werden const std::string& r2 = s1 + s1; // Okay: Lvalue-Referenz auf const verlängert Lebensdauer // r2 += "Test"; // Fehler: kann nicht durch Referenz auf const modifizieren std::string&& r3 = s1 + s1; // Okay: Rvalue-Referenz verlängert Lebensdauer r3 += "Test"; // Okay: kann durch Referenz auf non-const modifizieren std::cout << r3 << '\n'; } Noch wichtiger ist, dass wenn eine Funktion sowohl Rvalue-Referenz- als auch Lvalue-Referenz- Überladungen hat, die Rvalue-Referenz-Überladung an Rvalues (einschließlich sowohl Prvalues als auch Xvalues) bindet, während die Lvalue-Referenz-Überladung an Lvalues bindet:
Diesen Code ausführen
#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); // ruft f(int&) auf f(ci); // ruft f(const int&) auf f(3); // ruft f(int&&) auf // würde f(const int&) aufrufen, wenn f(int&&) Überladung nicht vorhanden wäre f(std::move(i)); // ruft f(int&&) auf // Rvalue-Referenzvariablen sind Lvalues, wenn sie in Ausdrücken verwendet werden int&& x = 1; f(x); // ruft f(int& x) auf f(std::move(x)); // ruft f(int&& x) auf } Dies ermöglicht es, Move-Konstruktoren , Move-Zuweisungsoperatoren und andere move-fähige Funktionen (z.B. std::vector::push_back() ) automatisch auszuwählen, wenn dies geeignet ist. 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); // bindet direkt an i2 Dies ermöglicht es, aus einem Objekt im Gültigkeitsbereich zu bewegen, das nicht mehr benötigt wird: std::vector<int> v{1, 2, 3, 4, 5}; std::vector<int> v2(std::move(v)); // bindet eine Rvalue-Referenz an v assert(v.empty()); WeiterleitungsreferenzenForwarding-Referenzen sind eine spezielle Art von Referenzen, die die Wertkategorie eines Funktionsarguments erhalten, wodurch es möglich wird, es mittels Forwarding durch std::forward weiterzuleiten. Forwarding-Referenzen sind entweder:
1)
Funktionsparameter einer Funktionsvorlage, deklariert als Rvalue-Referenz auf cv-unqualifizierten
Typ-Template-Parameter
derselben Funktionsvorlage:
template<class T> int f(T&& x) // x ist eine Weiterleitungsreferenz { return g(std::forward<T>(x)); // und kann daher weitergeleitet werden } int main() { int i; f(i); // Argument ist Lvalue, ruft f<int&>(int&) auf, std::forward<int&>(x) ist Lvalue f(0); // Argument ist Rvalue, ruft f<int>(int&&) auf, std::forward<int>(x) ist Rvalue } template<class T> int g(const T&& x); // x ist keine Weiterleitungsreferenz: const T ist nicht cv-unqualifiziert template<class T> struct A { template<class U> A(T&& x, U&& y, int* p); // x ist keine Weiterleitungsreferenz: T ist kein // Typ-Template-Parameter des Konstruktors, // aber y ist eine Weiterleitungsreferenz };
2)
auto
&&
außer wenn von einer geschweiften Klammer-Initialisierungsliste abgeleitet
oder, wenn es einen Template-Parameter einer Klassenvorlage während der
Klassentemplate-Argumentableitung
repräsentiert
(seit C++17)
:
auto&& vec = foo(); // foo() kann ein Lvalue oder Rvalue sein, vec ist eine Weiterleitungsreferenz auto i = std::begin(vec); // funktioniert in beiden Fällen (*i)++; // funktioniert in beiden Fällen g(std::forward<decltype(vec)>(vec)); // leitet weiter und erhält die Wertkategorie for (auto&& x: f()) { // x ist eine Weiterleitungsreferenz; dies ist eine gängige Methode für Range-for in generischem Code } auto&& z = {1, 2, 3}; // *keine* Weiterleitungsreferenz (Sonderfall für Initialisierungslisten) Siehe auch template argument deduction und std::forward . |
(seit C++11) |
Hängende Referenzen
Obwohl Referenzen bei der Initialisierung immer auf gültige Objekte oder Funktionen verweisen, ist es möglich, ein Programm zu erstellen, in dem die Lebensdauer des referenzierten Objekts endet, die Referenz jedoch weiterhin zugänglich bleibt ( dangling ).
Gegeben sei ein Ausdruck expr vom Referenztyp und sei target das Objekt oder die Funktion, auf die die Referenz verweist:
- Wenn ein Zeiger auf target im Kontext der Auswertung von expr gültig wäre, dann bezeichnet das Ergebnis target .
- Andernfalls ist das Verhalten undefiniert.
std::string& f() { std::string s = "Example"; return s; // verlässt den Gültigkeitsbereich von s: // dessen Destruktor wird aufgerufen und sein Speicher freigegeben } std::string& r = f(); // hängende Referenz std::cout << r; // undefiniertes Verhalten: Lesen von einer hängenden Referenz std::string s = f(); // undefiniertes Verhalten: Kopierinitialisierung von einer hängenden Referenz
Beachten Sie, dass Rvalue-Referenzen und Lvalue-Referenzen auf const die Lebensdauer temporärer Objekte 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 seiner Lebensdauer auf eingeschränkte Weise verwendet werden und kann gültig werden, wenn das Objekt im selben Speicher neu erstellt wird (siehe Access outside of lifetime für Details).
Typ-unzugängliche Referenzen
Der Versuch, eine Referenz auf ein Objekt zu binden, bei dem der konvertierte Initialisierer ein Lvalue (bis C++11) ein Glvalue (seit C++11) ist, durch den das Objekt nicht typzugänglich ist, führt zu undefiniertem Verhalten:
char x alignas(int); int& ir = *reinterpret_cast<int*>(&x); // undefiniertes Verhalten: // Initialisierer referenziert char-Objekt
Aufruf-inkompatible Referenzen
Der Versuch, eine Referenz auf eine Funktion zu binden, bei der 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); // undefiniertes Verhalten: // Initialisierer bezieht sich auf void(int)-Funktion
Hinweise
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_rvalue_references
|
200610L
|
(C++11) | Rvalue-Referenzen |
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 453 | C++98 | es war unklar, auf welches Objekt oder welche Funktion eine Referenz nicht gebunden werden kann | 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 | nicht erlaubt |
| CWG 2933 | C++98 | das Verhalten beim Zugriff auf hängende Referenzen war unklar | klargestellt |
Externe Links
| Thomas Becker, 2013 - C++ Rvalue References Explained |