Reference initialization
Enlaza una referencia a un objeto.
Contenidos |
Sintaxis
Inicialización no de lista
T
&
ref
=
target
;
T
|
(1) | ||||||||
T
&&
ref
=
target
;
T
|
(2) | (desde C++11) | |||||||
func-refpar
(
target
)
|
(3) | ||||||||
return
target
;
|
(4) | (dentro de la definición de func-refret ) | |||||||
Class
::
Class
(
...
) :
ref-member
(
target
) {
...
}
|
(5) | (dentro de la definición de Class ) | |||||||
Inicialización de lista ordinaria (desde C++11)
T
&
ref
= {
arg1
,
arg2
,
...
};
T
|
(1) | ||||||||
T
&&
ref
= {
arg1
,
arg2
,
...
};
T
|
(2) | ||||||||
func-refpar
({
arg1
,
arg2
,
...
});
|
(3) | ||||||||
Inicialización de lista designada (desde 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) | ||||||||
`, `
` y `
Una referencia a
T
puede ser inicializada con un objeto de tipo
T
, una función de tipo
T
, o un objeto implícitamente convertible a
T
. Una vez inicializada, una referencia no puede ser reasignada (cambiada) para referirse a otro objeto.
Las referencias se inicializan en las siguientes situaciones:
Explicación
| T | - | el tipo referenciado |
| ref | - | la variable de referencia a inicializar |
| target | - | la expresión inicializadora utilizada |
| func-refpar | - |
una función con un parámetro de tipo referencia (
T
&
o
T
&&
(desde C++11)
)
|
| func-refret | - |
una función cuyo tipo de retorno es un tipo referencia (
T
&
o
T
&&
(desde C++11)
)
|
| Class | - | un nombre de clase |
| ref-member | - |
un miembro de datos no estático de tipo referencia (
T
&
o
T
&&
(desde C++11)
) de
Class
|
| des1 , des2 , ... | - | designadores |
| arg1 , arg2 , ... | - | los inicializadores en listas de inicialización |
Definiciones
Para dos tipos
T1
y
T2
:
-
Dadas las versiones sin calificación cv de
T1yT2comoU1yU2respectivamente, siU1es similar aU2, oU1es una clase base deU2,T1está relacionado por referencia conT2. -
Si un valor pr de tipo "puntero a
T2" puede convertirse al tipo "puntero aT1" mediante una secuencia de conversión estándar,T1es compatible por referencia conT2.
Reglas de inicialización
|
Si una inicialización de referencia utiliza una lista ordinaria o designada (desde C++20) , se siguen las reglas de inicialización de lista . |
(desde C++11) |
Para la inicialización de referencias no de lista, dado el tipo del
target
como
U
, la referencia o bien se
enlaza directamente
al
target
o se enlaza a un valor de tipo
T
convertido desde el
target
. Primero se considera el enlace directo, seguido del enlace indirecto; si ninguno de los enlaces está disponible, el programa está mal formado.
En todos los casos donde la relación de compatibilidad de referencia de dos tipos se utiliza para establecer la validez de un enlace de referencia y la secuencia de conversión estándar sería incorrecta, un programa que requiera dicho enlace es incorrecto.
Enlace directo
Si se cumplen todas las siguientes condiciones:
- La referencia a ser inicializada es una referencia lvalue.
- target es un lvalue que no es un campo de bits .
-
Tes compatible por referencia conU.
Entonces la referencia se enlaza a target , o a su subobjeto de clase base apropiado:
double d = 2.0; double& rd = d; // rd se refiere a d const double& rcd = d; // rcd se refiere a d struct A {}; struct B : A {} b; A& ra = b; // ra se refiere al subobjeto A en b const A& rca = b; // rca se refiere al subobjeto A en b
De lo contrario, si se cumplen todas las siguientes condiciones:
- La referencia a inicializar es una referencia lvalue.
-
Ues un tipo clase. -
Tno está relacionado por referencia conU. -
target
puede convertirse a un lvalue de tipo
Vtal queTsea compatible por referencia conV.
Entonces la referencia se enlaza al resultado lvalue de la conversión, o a su subobjeto de clase base apropiado:
struct A {}; struct B : A { operator int&(); }; int& ir = B(); // ir hace referencia al resultado de B::operator int&
De lo contrario, si la referencia a inicializar es una referencia de lvalue, y
T
no está calificado como const o está calificado como volatile, el programa está mal formado:
double& rd2 = 2.0; // error: no es un lvalue y la referencia no es const int i = 2; double& rd3 = i; // error: tipo incompatible y la referencia no es const
De lo contrario, si se cumplen todas las siguientes condiciones:
- target es un valor de cualquiera de las siguientes categorías:
|
(hasta C++11) |
|
(desde C++11)
(hasta C++17) |
|
(desde C++17) |
-
Tes compatible por referencia conU.
Entonces la referencia se enlaza a target , o a su subobjeto de clase base apropiado:
struct A {}; struct B : A {}; extern B f(); const A& rca2 = f(); // vinculado al subobjeto A del valor temporal B. A&& rra = f(); // igual que el anterior int i2 = 42; int&& rri = static_cast<int&&>(i2); // vinculado directamente a i2
|
Si
target
es un prvalue,
la materialización temporal
se aplica a él, considerando el tipo del prvalue como el tipo ajustado
En este caso, la referencia se vincula al objeto resultante, o a su subobjeto de clase base apropiado. |
(desde C++17) |
De lo contrario, si se cumplen todas las siguientes condiciones:
-
Ues un tipo de clase. -
Tno está relacionado por referencia conU. -
target
puede convertirse a un valor
v
de tipo
Vtal queTsea compatible por referencia conV, donde v es de cualquiera de las siguientes categorías:
|
(hasta C++11) |
|
(desde C++11)
(hasta C++17) |
|
(desde C++17) |
Entonces la referencia se enlaza al resultado de la conversión, o a su subobjeto de clase base apropiado:
struct A {}; struct B : A {}; struct X { operator B(); } x; const A& r = x; // vinculado al subobjeto A del resultado de la conversión B&& rrb = x; // vinculado directamente al resultado de la conversión
|
Si el resultado de la conversión es un prvalue,
materialización temporal
se aplica a él, considerando el tipo del prvalue como el tipo ajustado
En este caso, la referencia se enlaza al objeto resultado, o a su subobjeto de clase base apropiado. |
(desde C++17) |
Enlace indirecto
Si el enlace directo no está disponible, se considera el enlace indirecto. En este caso,
T
no puede estar relacionado por referencia con
U
.
Si
T
o
U
es un tipo clase, se consideran conversiones definidas por el usuario usando las reglas para
inicialización por copia
de un objeto de tipo
T
mediante conversión definida por el usuario. El programa está mal formado si la correspondiente inicialización por copia no referencial estaría mal formada. El resultado de la llamada a la función de conversión, como se describe para la
inicialización por copia
no referencial, se utiliza luego para inicializar directamente la referencia. Para esta inicialización directa, no se consideran conversiones definidas por el usuario.
|
De lo contrario, se crea un temporal de tipo
|
(until C++17) |
|
De lo contrario,
target
se convierte implícitamente a un prvalue de tipo "cv-unqualified
|
(since C++17) |
const std::string& rs = "abc"; // rs se refiere a una copia temporal inicializada desde un array de caracteres const double& rcd2 = 2; // rcd2 se refiere a un temporal con valor 2.0 int i3 = 2; double&& rrd3 = i3; // rrd3 se refiere a un temporal con valor 2.0
Vida útil de un temporal
Siempre que una referencia se vincula a un objeto temporal o a un subobjeto del mismo, el tiempo de vida del objeto temporal se extiende para coincidir con el tiempo de vida de la referencia (consulte excepciones de tiempo de vida de objeto temporal ), donde el objeto temporal o su subobjeto se denota mediante una de las siguientes expresiones:
|
(hasta C++17) |
| (desde C++17) |
- una expresión entre paréntesis ( e ) , donde e es una de estas expresiones,
- una expresión de subíndice incorporada de la forma a [ n ] o n [ a ] , donde a es un array y es una de estas expresiones,
- una expresión de acceso a miembro de clase de la forma e. m , donde e es una de estas expresiones y m designa un miembro de datos no estático de tipo objeto,
- una operación de puntero-a-miembro de la forma e. * mp , donde e es una de estas expresiones y mp es un puntero a miembro de datos,
-
una conversión
const_cast,static_cast,dynamic_cast, oreinterpret_castsin una conversión definida por el usuario que convierta una de estas expresiones al glvalue que se refiere al objeto designado por el operando, o a su objeto completo o un subobjeto del mismo (una expresión de conversión explícita se interpreta como una secuencia de estas conversiones), - una expresión condicional de la forma cond ? e1 : e2 que es un glvalue, donde e1 o e2 es una de estas expresiones, o
- una expresión de coma incorporada de la forma x, e que es un glvalue, donde e es una de estas expresiones.
Existen las siguientes excepciones a esta regla de duración:
|
(hasta C++26) |
- un temporal vinculado a un parámetro de referencia en una llamada de función existe hasta el final de la expresión completa que contiene esa llamada de función: si la función retorna una referencia, que sobrevive a la expresión completa, se convierte en una referencia colgante.
|
(since C++11) |
struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference |
(since C++20) |
En general, la vida útil de un temporal no puede extenderse más al "pasarlo": una segunda referencia, inicializada desde la variable de referencia o miembro de datos al que estaba vinculado el temporal, no afecta su vida útil.
Notas
Las referencias aparecen sin inicializadores solo en la declaración de parámetros de función, en la declaración del tipo de retorno de función, en la declaración de un miembro de clase, y con el
extern
especificador.
Hasta la resolución de CWG issue 1696 , se permitía que un temporal se vinculara a un miembro de referencia en una lista de inicialización initializer list del constructor, y persistía solo hasta que el constructor finalizaba, no mientras existiera el objeto. Dicha inicialización es incorrecta desde CWG 1696 , aunque muchos compiladores aún la admiten (una excepción notable es clang).
Ejemplo
#include <sstream> #include <utility> struct S { int mi; const std::pair<int, int>& mp; // miembro de referencia }; void foo(int) {} struct A {}; struct B : A { int n; operator int&() { return n; } }; B bar() { return B(); } //int& bad_r; // error: sin inicializador extern int& ext_r; // OK int main() { // Lvalues int n = 1; int& r1 = n; // referencia lvalue al objeto n const int& cr(n); // la referencia puede ser más cv-calificada volatile int& cv{n}; // se puede usar cualquier sintaxis de inicialización int& r2 = r1; // otra referencia lvalue al objeto n // int& bad = cr; // error: menos cv-calificada int& r3 = const_cast<int&>(cr); // se necesita const_cast void (&rf)(int) = foo; // referencia lvalue a función int ar[3]; int (&ra)[3] = ar; // referencia lvalue a array B b; A& base_ref = b; // referencia al subobjeto base int& converted_ref = b; // referencia al resultado de una conversión // Rvalues // int& bad = 1; // error: no se puede enlazar ref lvalue a rvalue const int& cref = 1; // enlazado a rvalue int&& rref = 1; // enlazado a rvalue const A& cref2 = bar(); // referencia al subobjeto A del temporal B A&& rref2 = bar(); // igual int&& xref = static_cast<int&&>(n); // enlazar directamente a n // int&& copy_ref = n; // error: no se puede enlazar a un lvalue double&& copy_ref = n; // enlazar a un temporal rvalue con valor 1.0 // Restricciones en tiempos de vida de temporales // std::ostream& buf_ref = std::ostringstream() << 'a'; // el temporal ostringstream se enlazó al operando izquierdo // de operator<< pero su tiempo de vida terminó en el punto y coma // por lo que buf_ref es una referencia colgante S a {1, {2, 3}}; // par temporal {2, 3} enlazado al miembro referencia // a.mp y su tiempo de vida se extiende para coincidir // con el tiempo de vida del objeto a S* p = new S{1, {2, 3}}; // par temporal {2, 3} enlazado a la referencia // miembro p->mp, pero su tiempo de vida terminó en el punto y coma // p->mp es una referencia colgante delete p; // Imitar [[maybe_unused]] aplicado a las siguientes variables: [](...){} ( cv, r2, r3, rf, ra, base_ref, converted_ref, a, cref, rref, cref2, rref2, copy_ref, xref ); }
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 391 | C++98 |
inicializar una referencia a tipo calificado-const con un rvalue de tipo clase
podría crear un temporal, y se requería un constructor de esa clase para copiar el rvalue en ese temporal |
no se crea temporal,
no se requiere constructor |
| CWG 450 | C++98 |
una referencia a array calificado-const no podía
inicializarse con un rvalue de array compatible-con-referencia |
permitido |
| CWG 589 | C++98 | una referencia no podía vincularse directamente a un rvalue de array o clase | permitido |
| CWG 656 | C++98 |
una referencia a tipo calificado-const inicializada con un tipo que no es
compatible-con-referencia pero tiene una función de conversión a un tipo compatible-con-referencia se vinculaba a un temporal copiado del valor de retorno (o su subobjeto de clase base) de la función de conversión |
se vincula directamente al valor
de retorno (o su subobjeto de clase base) |
| CWG 1287 | C++11 |
la conversión desde
target
de tipo clase a otro tipo
compatible-con-referencia solo podía ser implícita |
permitir conversiones
explícitas |
| CWG 1295 | C++11 | una referencia podía vincularse a un xvalue de campo-de-bits | prohibido |
| CWG 1299 | C++98 | la definición de temporal no era clara | aclarada |
| CWG 1571 | C++98 |
las conversiones definidas por el usuario en el
enlace indirecto no consideraban el tipo de target |
considerado |
| CWG 1604 | C++98 | las conversiones definidas por el usuario no se consideraban en el enlace indirecto | consideradas |
| CWG 2352 | C++98 | la compatibilidad de referencia no consideraba las conversiones de calificación | consideradas |
| CWG 2481 | C++17 |
la calificación cv no se añadía al tipo resultante
de la materialización temporal en el enlace indirecto |
añadida |
| CWG 2657 | C++17 |
la calificación cv no se añadía al tipo resultante
de la materialización temporal en el enlace directo |
añadida |
| CWG 2801 | C++98 | se permitían tipos relacionados-con-referencia para el enlace indirecto | prohibido |