Reference initialization
Lie une référence à un objet.
Table des matières |
Syntaxe
Initialisation non-liste
T
&
ref
=
target
;
T
|
(1) | ||||||||
T
&&
ref
=
target
;
T
|
(2) | (depuis C++11) | |||||||
func-refpar
(
target
)
|
(3) | ||||||||
return
target
;
|
(4) | (dans la définition de func-refret ) | |||||||
Class
::
Class
(
...
) :
ref-member
(
target
) {
...
}
|
(5) | (dans la définition de Class ) | |||||||
Initialisation de liste ordinaire (depuis C++11)
T
&
ref
= {
arg1
,
arg2
,
...
};
T
|
(1) | ||||||||
T
&&
ref
= {
arg1
,
arg2
,
...
};
T
|
(2) | ||||||||
func-refpar
({
arg1
,
arg2
,
...
});
|
(3) | ||||||||
` tags (preserved as-is)
- C++ specific terms (preserved as-is)
- Generic programming terms (T, ref, arg1, arg2, func-refpar) that should remain in English for technical accuracy
There is no translatable natural language text in this content. The structure and technical content remain identical to the original.
Initialisation de liste désignée (depuis C++20)
T
&
ref
= {.
des1
=
arg1
, .
des2
{
arg2
}
...
};
T
|
(1) | ||||||||
T
&&
ref
= {.
des1
=
arg1
, .
des2
{
arg2
}
...
};
T
|
(2) | ||||||||
func-refpar
({.
des1
=
arg1
, .
des2
{
arg2
}
...
});
|
(3) | ||||||||
`, `
` et `
Une référence à
T
peut être initialisée avec un objet de type
T
, une fonction de type
T
, ou un objet implicitement convertible en
T
. Une fois initialisée, une référence ne peut pas être réassignée (modifiée) pour faire référence à un autre objet.
Les références sont initialisées dans les situations suivantes :
Explication
| T | - | le type référencé |
| ref | - | la variable référence à initialiser |
| target | - | l'expression d'initialisation utilisée |
| func-refpar | - |
une fonction avec un paramètre de type référence (
T
&
ou
T
&&
(depuis C++11)
)
|
| func-refret | - |
une fonction dont le type de retour est un type référence (
T
&
ou
T
&&
(depuis C++11)
)
|
| Class | - | un nom de classe |
| ref-member | - |
un membre de données non statique de type référence (
T
&
ou
T
&&
(depuis C++11)
) de
Class
|
| des1 , des2 , ... | - | désignateurs |
| arg1 , arg2 , ... | - | les initialiseurs dans les listes d'initialisation |
Définitions
Pour deux types
T1
et
T2
:
-
Étant donné les versions non qualifiées cv de
T1etT2commeU1etU2respectivement, siU1est similaire àU2, ou siU1est une classe de base deU2,T1est lié par référence àT2. -
Si une prvalue de type « pointeur vers
T2» peut être convertie en type « pointeur versT1» via une séquence de conversion standard,T1est compatible par référence avecT2.
Règles d'initialisation
|
Si une initialisation de référence utilise une liste ordinaire ou désignée (depuis C++20) , les règles de l'initialisation de liste sont suivies. |
(depuis C++11) |
Pour l'initialisation de référence non-liste, étant donné le type de
target
comme
U
, la référence soit
se lie directement
à
target
soit se lie à une valeur de type
T
convertie à partir de
target
. La liaison directe est considérée en premier, suivie par la liaison indirecte, si aucune liaison n'est disponible, le programme est mal formé.
Dans tous les cas où la relation de compatibilité de référence de deux types est utilisée pour établir la validité d'une liaison de référence et que la séquence de conversion standard serait mal formée, un programme qui nécessite une telle liaison est mal formé.
Liaison directe
Si toutes les conditions suivantes sont satisfaites :
- La référence à initialiser est une référence lvalue.
- target est une lvalue non- champ de bits .
-
Test compatible par référence avecU.
Alors la référence se lie à target , ou à son sous-objet de classe de base approprié :
double d = 2.0; double& rd = d; // rd fait référence à d const double& rcd = d; // rcd fait référence à d struct A {}; struct B : A {} b; A& ra = b; // ra fait référence au sous-objet A dans b const A& rca = b; // rca fait référence au sous-objet A dans b
Sinon, si toutes les conditions suivantes sont satisfaites :
- La référence à initialiser est une référence lvalue.
-
Uest un type classe. -
Tn'est pas lié par référence àU. -
target
peut être converti en une lvalue de type
Vde telle sorte queTsoit compatible par référence avecV.
Alors la référence se lie au résultat lvalue de la conversion, ou à son sous-objet de classe de base approprié :
struct A {}; struct B : A { operator int&(); }; int& ir = B(); // ir fait référence au résultat de B::operator int&
Sinon, si la référence à initialiser est une référence lvalue, et
T
n'est pas qualifié const ou est qualifié volatile, le programme est mal formé :
double& rd2 = 2.0; // erreur : pas un lvalue et la référence n'est pas const int i = 2; double& rd3 = i; // erreur : incompatibilité de type et la référence n'est pas const
Sinon, si toutes les conditions suivantes sont satisfaites :
- target est une valeur de l'une des catégories suivantes :
|
(jusqu'à C++11) |
|
(depuis C++11)
(jusqu'à C++17) |
|
(depuis C++17) |
-
Test compatible par référence avecU.
Alors la référence se lie à target , ou à son sous-objet de classe de base approprié :
struct A {}; struct B : A {}; extern B f(); const A& rca2 = f(); // lié au sous-objet A de la valeur B temporaire. A&& rra = f(); // identique au cas précédent int i2 = 42; int&& rri = static_cast<int&&>(i2); // lié directement à i2
|
Si
target
est une prvalue,
la matérialisation temporaire
lui est appliquée, en considérant le type de la prvalue comme étant le type ajusté
Dans ce cas, la référence se lie à l'objet résultant, ou à son sous-objet de classe de base approprié. |
(depuis C++17) |
Sinon, si toutes les conditions suivantes sont satisfaites :
-
Uest un type de classe. -
Tn'est pas lié par référence àU. -
target
peut être converti en une valeur
v
de type
Vtelle queTsoit compatible par référence avecV, où v est de l'une des catégories suivantes :
|
(jusqu'à C++11) |
|
(depuis C++11)
(jusqu'à C++17) |
|
(depuis C++17) |
Alors la référence se lie au résultat de la conversion, ou à son sous-objet de classe de base approprié :
struct A {}; struct B : A {}; struct X { operator B(); } x; const A& r = x; // lié au sous-objet A du résultat de la conversion B&& rrb = x; // lié directement au résultat de la conversion
|
Si le résultat de la conversion est une prvalue,
la matérialisation temporaire
lui est appliquée, en considérant le type de la prvalue comme étant le type ajusté
Dans ce cas, la référence se lie à l'objet résultat, ou à son sous-objet de classe de base approprié. |
(depuis C++17) |
Liaison indirecte
Si la liaison directe n'est pas disponible, la liaison indirecte est considérée. Dans ce cas,
T
ne peut pas être lié par référence à
U
.
Si
T
ou
U
est un type classe, les conversions définies par l'utilisateur sont considérées en utilisant les règles pour
l'initialisation par copie
d'un objet de type
T
par conversion définie par l'utilisateur. Le programme est mal formé si l'initialisation par copie non-référence correspondante serait mal formée. Le résultat de l'appel à la fonction de conversion, comme décrit pour l'
initialisation par copie
non-référence, est ensuite utilisé pour initialiser directement la référence. Pour cette initialisation directe, les conversions définies par l'utilisateur ne sont pas considérées.
|
Sinon, un temporaire de type
|
(until C++17) |
|
Sinon,
target
est implicitement converti en une prvalue de type « cv-unqualified
|
(since C++17) |
const std::string& rs = "abc"; // rs fait référence à une copie temporaire initialisée à partir d'un tableau de caractères const double& rcd2 = 2; // rcd2 fait référence à une temporaire avec la valeur 2.0 int i3 = 2; double&& rrd3 = i3; // rrd3 fait référence à une temporaire avec la valeur 2.0
Durée de vie d'un temporaire
Chaque fois qu'une référence est liée à un objet temporaire ou à un sous-objet de celui-ci, la durée de vie de l'objet temporaire est prolongée pour correspondre à la durée de vie de la référence (vérifiez les exceptions de durée de vie des objets temporaires ), où l'objet temporaire ou son sous-objet est désigné par l'une des expressions suivantes :
|
(jusqu'en C++17) |
| (depuis C++17) |
- une expression entre parenthèses ( e ) , où e est l'une de ces expressions,
- une expression d'indice intégrée de la forme a [ n ] ou n [ a ] , où a est un tableau et est l'une de ces expressions,
- une expression d'accès membre de classe de la forme e. m , où e est l'une de ces expressions et m désigne un membre de données non statique de type objet,
- une opération pointeur-vers-membre de la forme e. * mp , où e est l'une de ces expressions et mp est un pointeur vers un membre de données,
-
une conversion
const_cast,static_cast,dynamic_cast, oureinterpret_castsans conversion définie par l'utilisateur qui convertit l'une de ces expressions en une glvalue se référant à l'objet désigné par l'opérande, ou à son objet complet ou un sous-objet de celui-ci (une expression de cast explicite est interprétée comme une séquence de ces casts), - une expression conditionnelle de la forme cond ? e1 : e2 qui est une glvalue, où e1 ou e2 est l'une de ces expressions, ou
- une expression virgule intégrée de la forme x, e qui est une glvalue, où e est l'une de ces expressions.
Il existe les exceptions suivantes à cette règle de durée de vie :
|
(jusqu'à C++26) |
- un temporaire lié à un paramètre de référence dans un appel de fonction existe jusqu'à la fin de l'expression complète contenant cet appel de fonction : si la fonction retourne une référence, qui survit à l'expression complète, elle devient une référence pendante.
|
(depuis C++11) |
struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference |
(depuis C++20) |
En général, la durée de vie d'un temporaire ne peut pas être prolongée davantage en « le transmettant » : une seconde référence, initialisée à partir de la variable de référence ou du membre de données auquel le temporaire était lié, n'affecte pas sa durée de vie.
Notes
Les références apparaissent sans initialiseurs uniquement dans la déclaration de paramètre de fonction, dans la déclaration de type de retour de fonction, dans la déclaration d'un membre de classe, et avec le
extern
spécificateur.
Jusqu'à la résolution de CWG issue 1696 , un temporaire était autorisé à être lié à un membre référence dans une initializer list d'un constructeur, et il persistait uniquement jusqu'à la sortie du constructeur, et non pas aussi longtemps que l'objet existe. Une telle initialisation est non conforme depuis CWG 1696 , bien que de nombreux compilateurs la prennent encore en charge (une exception notable est clang).
Exemple
#include <sstream> #include <utility> struct S { int mi; const std::pair<int, int>& mp; // membre référence }; void foo(int) {} struct A {}; struct B : A { int n; operator int&() { return n; } }; B bar() { return B(); } //int& bad_r; // erreur : pas d'initialiseur extern int& ext_r; // OK int main() { // Lvalues int n = 1; int& r1 = n; // référence lvalue à l'objet n const int& cr(n); // la référence peut être plus cv-qualifiée volatile int& cv{n}; // toute syntaxe d'initialisation peut être utilisée int& r2 = r1; // autre référence lvalue à l'objet n // int& bad = cr; // erreur : moins cv-qualifiée int& r3 = const_cast<int&>(cr); // const_cast est nécessaire void (&rf)(int) = foo; // référence lvalue à fonction int ar[3]; int (&ra)[3] = ar; // référence lvalue à tableau B b; A& base_ref = b; // référence au sous-objet de base int& converted_ref = b; // référence au résultat d'une conversion // Rvalues // int& bad = 1; // erreur : impossible de lier une référence lvalue à une rvalue const int& cref = 1; // liée à une rvalue int&& rref = 1; // liée à une rvalue const A& cref2 = bar(); // référence au sous-objet A du temporaire B A&& rref2 = bar(); // idem int&& xref = static_cast<int&&>(n); // liaison directe à n // int&& copy_ref = n; // erreur : impossible de lier à une lvalue double&& copy_ref = n; // liée à un temporaire rvalue avec la valeur 1.0 // Restrictions sur la durée de vie des temporaires // std::ostream& buf_ref = std::ostringstream() << 'a'; // le temporaire ostringstream était lié à l'opérande gauche // de operator<< mais sa durée de vie s'est terminée au point-virgule donc // buf_ref est une référence pendante S a {1, {2, 3}}; // paire temporaire {2, 3} liée au membre référence // a.mp et sa durée de vie est étendue pour correspondre // à la durée de vie de l'objet a S* p = new S{1, {2, 3}}; // paire temporaire {2, 3} liée au membre référence // p->mp, mais sa durée de vie s'est terminée au point-virgule // p->mp est une référence pendante delete p; // Imiter [[maybe_unused]] appliqué aux variables suivantes : [](...){} ( cv, r2, r3, rf, ra, base_ref, converted_ref, a, cref, rref, cref2, rref2, copy_ref, xref ); }
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 tel que publié | Comportement corrigé |
|---|---|---|---|
| CWG 391 | C++98 |
initialiser une référence vers un type qualifié const avec une rvalue
de type classe pouvait créer un temporaire, et un constructeur de cette classe était requis pour copier la rvalue dans ce temporaire |
aucun temporaire n'est
créé, le constructeur n'est pas requis |
| CWG 450 | C++98 |
une référence vers un tableau qualifié const ne pouvait pas
être initialisée avec une rvalue de tableau compatible par référence |
autorisé |
| CWG 589 | C++98 | une référence ne pouvait pas se lier directement à une rvalue de tableau ou de classe | autorisé |
| CWG 656 | C++98 |
une référence vers un type qualifié const initialisée avec un type non
compatible par référence mais ayant une fonction de conversion vers un type compatible par référence était liée à un temporaire copié depuis la valeur de retour (ou son sous-objet de classe de base) de la fonction de conversion |
liée directement à la valeur
de retour (ou son sous-objet de classe de base) |
| CWG 1287 | C++11 |
la conversion du
target
de type classe vers un autre
type compatible par référence ne pouvait être qu'implicite |
autoriser les conversions
explicites |
| CWG 1295 | C++11 | une référence pouvait se lier à une xvalue de champ de bits | interdit |
| CWG 1299 | C++98 | la définition de temporaire était peu claire | clarifiée |
| CWG 1571 | C++98 |
les conversions définies par l'utilisateur dans la liaison
indirecte ne prenaient pas en compte le type du target |
pris en compte |
| CWG 1604 | C++98 | les conversions définies par l'utilisateur n'étaient pas considérées dans la liaison indirecte | considérées |
| CWG 2352 | C++98 | la compatibilité par référence ne prenait pas en compte les conversions de qualification | prise en compte |
| CWG 2481 | C++17 |
la qualification cv n'était pas ajoutée au type de résultat
de la matérialisation de temporaire dans la liaison indirecte |
ajoutée |
| CWG 2657 | C++17 |
la qualification cv n'était pas ajoutée au type de résultat
de la matérialisation de temporaire dans la liaison directe |
ajoutée |
| CWG 2801 | C++98 | les types liés par référence étaient autorisés pour la liaison indirecte | interdit |