Namespaces
Variants

Reference declaration

From 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

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)
1) Lvalue-Referenzdeklarator : die Deklaration S & D ; deklariert D als eine Lvalue-Referenz auf den durch decl-specifier-seq S bestimmten Typ.
2) Rvalue-Referenzdeklarator : Die Deklaration S && D ; deklariert 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 Collapsing

Es 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 T&& in einem Funktions-Template verwendet wird, die Regeln, die std::forward ermöglichen.)

(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-Referenzen

Rvalue-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):

#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:

#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());
**Übersetzungserläuterungen:** - Der Kommentar `// binds an rvalue reference to v` wurde präzise als `// bindet eine Rvalue-Referenz an v` übersetzt - Alle HTML-Tags, Attribute und Code-Blöcke wurden unverändert beibehalten - C++-spezifische Begriffe wie `std::vector`, `std::move`, `assert` und `empty()` wurden nicht übersetzt - Die numerischen Werte und Syntax-Elemente bleiben original erhalten

Weiterleitungsreferenzen

Forwarding-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