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

Déclare une variable nommée comme une référence, c'est-à-dire un alias vers un objet ou une fonction déjà existant.

Table des matières

Syntaxe

Une déclaration de variable de référence est toute déclaration simple dont le déclarateur a la forme

& attr  (facultatif) declarator (1)
&& attr  (facultatif) declarator (2) (depuis C++11)
1) Déclarateur de référence Lvalue : la déclaration S & D ; déclare D comme une référence Lvalue vers le type déterminé par decl-specifier-seq S .
2) Déclarateur de référence à valeur droite : la déclaration S && D ; déclare D comme une référence à valeur droite vers le type déterminé par decl-specifier-seq S .
declarator - tout déclarateur sauf le déclarateur de référence, déclarateur de tableau , et déclarateur de pointeur (il n'existe pas de références vers des références, de tableaux de références, ou de pointeurs vers des références)
attr - (depuis C++11) liste d' attributs

Une référence doit être initialisée pour se référer à un objet ou une fonction valide : voir reference initialization .

Le type « référence à (éventuellement qualifié cv) void » ne peut pas être formé.

Les types référence ne peuvent pas être cv-qualifiés au niveau supérieur ; il n'existe pas de syntaxe pour cela dans la déclaration, et si une qualification est ajoutée à un typedef-name ou decltype specifier, (depuis C++11) ou type template parameter , elle est ignorée.

Les références ne sont pas des objets ; elles n'occupent pas nécessairement de stockage, bien que le compilateur puisse allouer du stockage si cela est nécessaire pour implémenter la sémantique souhaitée (par exemple, un membre de données non statique de type référence augmente généralement la taille de la classe de la quantité nécessaire pour stocker une adresse mémoire).

Parce que les références ne sont pas des objets, il n'existe pas de tableaux de références, pas de pointeurs vers des références, et pas de références vers des références :

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

Référence écrasante

Il est permis de former des références à des références via des manipulations de types dans les templates ou les typedefs, auquel cas les règles de référence écrasante s'appliquent : une référence rvalue vers une référence rvalue s'écrase en référence rvalue, toutes les autres combinaisons forment une référence lvalue :

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

(Ceci, avec les règles spéciales pour la déduction d'arguments de template quand T&& est utilisé dans un template de fonction, forme les règles qui rendent std::forward possible.)

(depuis C++11)

Références Lvalue

Les références Lvalue peuvent être utilisées pour créer un alias d'un objet existant (éventuellement avec des qualifications cv différentes) :

#include <iostream>
#include <string>
int main()
{
    std::string s = "Ex";
    std::string& r1 = s;
    const std::string& r2 = s;
    r1 += "ample";           // modifie s
//  r2 += "!";               // erreur : impossible de modifier via une référence const
    std::cout << r2 << '\n'; // affiche s, qui contient maintenant "Example"
}

Ils peuvent également être utilisés pour implémenter la sémantique de passage par référence dans les appels de fonction :

#include <iostream>
#include <string>
void double_string(std::string& s)
{
    s += s; // 's' est le même objet que 'str' dans main()
}
int main()
{
    std::string str = "Test";
    double_string(str);
    std::cout << str << '\n';
}

Lorsque le type de retour d'une fonction est une référence lvalue, l'expression d'appel de fonction devient une expression lvalue :

#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';
}

Références Rvalue

Les références de rvalue peuvent être utilisées pour prolonger la durée de vie des objets temporaires (notez que les références de lvalue vers const peuvent également prolonger la durée de vie des objets temporaires, mais elles ne permettent pas de les modifier) :

#include <iostream>
#include <string>
int main()
{
    std::string s1 = "Test";
//  std::string&& r1 = s1;           // erreur : ne peut pas se lier à une lvalue
    const std::string& r2 = s1 + s1; // correct : référence à une lvalue const prolonge la durée de vie
//  r2 += "Test";                    // erreur : ne peut pas modifier via une référence const
    std::string&& r3 = s1 + s1;      // correct : référence à une rvalue prolonge la durée de vie
    r3 += "Test";                    // correct : peut modifier via une référence non-const
    std::cout << r3 << '\n';
}

Plus important encore, lorsqu'une fonction possède à la fois des surcharges de référence rvalue et de référence lvalue, la surcharge de référence rvalue se lie aux rvalues (incluant à la fois les prvalues et les xvalues), tandis que la surcharge de référence lvalue se lie aux lvalues :

#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);  // appelle f(int&)
    f(ci); // appelle f(const int&)
    f(3);  // appelle f(int&&)
           // appellerait f(const int&) si la surcharge f(int&&) n'était pas fournie
    f(std::move(i)); // appelle f(int&&)
    // les variables de référence rvalue sont des lvalues lorsqu'utilisées dans des expressions
    int&& x = 1;
    f(x);            // appelle f(int& x)
    f(std::move(x)); // appelle f(int&& x)
}

Cela permet aux constructeurs de déplacement , opérateurs d'affectation par déplacement et autres fonctions compatibles avec le déplacement (par exemple std::vector::push_back() ) d'être automatiquement sélectionnés lorsque cela est approprié.

Parce que les références de rvalue peuvent se lier aux xvalues, elles peuvent faire référence à des objets non temporaires :

int i2 = 42;
int&& rri = std::move(i2); // se lie directement à i2

Cela permet de déplacer un objet dans la portée qui n'est plus nécessaire :

std::vector<int> v{1, 2, 3, 4, 5};
std::vector<int> v2(std::move(v)); // lie une référence rvalue à v
assert(v.empty());

Références de transfert

Les références de transfert sont un type spécial de références qui préservent la catégorie de valeur d'un argument de fonction, permettant de le transférer au moyen de std::forward . Les références de transfert sont soit :

1) paramètre de fonction d'un modèle de fonction déclaré comme référence à valeur de rvalue vers un paramètre de modèle de type non qualifié cv type template parameter de ce même modèle de fonction :
template<class T>
int f(T&& x)                      // x est une référence de transfert
{
    return g(std::forward<T>(x)); // et peut donc être transférée
}
int main()
{
    int i;
    f(i); // l'argument est une lvalue, appelle f<int&>(int&), std::forward<int&>(x) est une lvalue
    f(0); // l'argument est une rvalue, appelle f<int>(int&&), std::forward<int>(x) est une rvalue
}
template<class T>
int g(const T&& x); // x n'est pas une référence de transfert : const T n'est pas sans qualificatif cv
template<class T>
struct A
{
    template<class U>
    A(T&& x, U&& y, int* p); // x n'est pas une référence de transfert : T n'est pas un
                             // paramètre de type du constructeur,
                             // mais y est une référence de transfert
};
2) auto && sauf lorsqu'il est déduit d'une liste d'initialisation entre accolades ou, lorsqu'il représente un paramètre de template d'un template de classe pendant la déduction d'arguments de template de classe (depuis C++17) :
auto&& vec = foo();       // foo() peut être une lvalue ou rvalue, vec est une référence de transfert
auto i = std::begin(vec); // fonctionne dans les deux cas
(*i)++;                   // fonctionne dans les deux cas
g(std::forward<decltype(vec)>(vec)); // transfère, en préservant la catégorie de valeur
for (auto&& x: f())
{
    // x est une référence de transfert ; c'est une manière courante d'utiliser la boucle for à portée dans le code générique
}
auto&& z = {1, 2, 3}; // *n'est pas* une référence de transfert (cas spécial pour les listes d'initialisation)

Voir aussi template argument deduction et std::forward .

(depuis C++11)

Références pendantes

Bien que les références se réfèrent toujours à des objets ou fonctions valides lors de l'initialisation, il est possible de créer un programme où la durée de vie de l'objet référencé se termine, mais la référence reste accessible ( dangling ).

Soit une expression expr de type référence et soit target l'objet ou la fonction désignée par la référence :

  • Si un pointeur vers target serait valide dans le contexte de l'évaluation de expr , le résultat désigne target .
  • Sinon, le comportement est indéfini.
std::string& f()
{
    std::string s = "Example";
    return s; // sort de la portée de s :
              // son destructeur est appelé et son stockage désalloué
}
std::string& r = f(); // référence pendante
std::cout << r;       // comportement indéfini : lecture depuis une référence pendante
std::string s = f();  // comportement indéfini : initialisation par copie depuis une référence pendante

Notez que les références de rvalue et les références de lvalue vers const prolongent la durée de vie des objets temporaires (voir Initialisation des références pour les règles et exceptions).

Si l'objet référencé a été détruit (par exemple, par un appel explicite au destructeur), mais que le stockage n'a pas été désalloué, une référence à l'objet hors durée de vie peut être utilisée de manière limitée, et peut devenir valide si l'objet est recréé dans le même stockage (voir Access outside of lifetime pour plus de détails).

Références de type inaccessible

Tenter de lier une référence à un objet où l'initialiseur converti est une lvalue (jusqu'en C++11) une glvalue (depuis C++11) par laquelle l'objet n'est pas accessible par le type entraîne un comportement indéfini :

char x alignas(int);
int& ir = *reinterpret_cast<int*>(&x); // comportement indéfini :
                                       // l'initialiseur fait référence à un objet char

Références d'appel incompatibles

Tenter de lier une référence à une fonction lorsque l'initialiseur converti est une lvalue (jusqu'en C++11) une glvalue (depuis C++11) dont le type n'est pas call-compatible avec le type de la définition de la fonction résulte en un comportement indéfini :

void f(int);
using F = void(float);
F& ir = *reinterpret_cast<F*>(&f); // comportement indéfini :
                                   // l'initialiseur fait référence à la fonction void(int)

Notes

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_rvalue_references 200610L (C++11) Rvalue references

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement publié Comportement corrigé
CWG 453 C++98 il n'était pas clair à quel objet ou fonction une référence ne pouvait pas être liée clarifié
CWG 1510 C++11 les références qualifiées cv ne pouvaient pas être formées dans l'opérande de decltype autorisé
CWG 2550 C++98 les paramètres pouvaient avoir le type « référence vers void » interdit
CWG 2933 C++98 le comportement d'accès aux références pendantes n'était pas clair clarifié

Liens externes

Thomas Becker, 2013 - Explication des références Rvalue en C++