Namespaces
Variants

Reference initialization

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

Enlaza una referencia a un objeto.

Contenidos

Sintaxis

Inicialización no de lista
T  & ref = target ;

T  & ref ( target );

(1)
T  && ref = target ;

T  && ref ( target );

(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  & ref { arg1 , arg2 , ... };

(1)
T  && ref = { arg1 , arg2 , ... };

T  && ref { arg1 , arg2 , ... };

(2)
func-refpar ({ arg1 , arg2 , ... }); (3)
Inicialización de lista designada (desde C++20)
T  & ref = {. des1 = arg1 , . des2 { arg2 } ... };

T  & ref {. des1 = arg1 , . des2 { arg2 } ... };

(1)
T  && ref = {. des1 = arg1 , . des2 { arg2 } ... };

T  && ref {. des1 = arg1 , . des2 { arg2 } ... };

(2)
func-refpar ({. des1 = arg1 , . des2 { arg2 } ... }); (3)
**Nota:** El texto dentro de las etiquetas ` `, `
` y `` se ha conservado sin traducción, así como todos los términos específicos de C++. Solo se han traducido los elementos de texto fuera de estas etiquetas siguiendo las instrucciones proporcionadas.

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:

1) Cuando una variable de lvalue reference con nombre es declarada con un inicializador.
2) Cuando una variable de rvalue reference con nombre se declara con un inicializador.
3) En una expresión de llamada a función, cuando el parámetro de la función tiene tipo referencia.
4) En la return statement, cuando la función retorna un tipo referencia. El programa está mal formado si la referencia retornada está ligada al resultado de una expresión temporal . (since C++26)
5) Cuando un miembro de datos no estático de tipo referencia se inicializa usando un inicializador de miembro .

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 T1 y T2 como U1 y U2 respectivamente, si U1 es similar a U2 , o U1 es una clase base de U2 , T1 está relacionado por referencia con T2 .
  • Si un valor pr de tipo "puntero a T2 " puede convertirse al tipo "puntero a T1 " mediante una secuencia de conversión estándar, T1 es compatible por referencia con T2 .

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 .
  • T es compatible por referencia con U .

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.
  • U es un tipo clase.
  • T no está relacionado por referencia con U .
  • target puede convertirse a un lvalue de tipo V tal que T sea compatible por referencia con V .

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:
  • rvalue
(hasta C++11)
  • non-bit-field xvalue
  • class prvalue
  • array prvalue
  • function lvalue
(desde C++11)
(hasta C++17)
  • non-bit-field rvalue
  • function lvalue
(desde C++17)
  • T es compatible por referencia con U .

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 P .

  • P se ajusta a partir del tipo de target (es decir, U ) añadiendo la calificación cv de T a él.

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:

  • U es un tipo de clase.
  • T no está relacionado por referencia con U .
  • target puede convertirse a un valor v de tipo V tal que T sea compatible por referencia con V , donde v es de cualquiera de las siguientes categorías:
  • rvalue
(hasta C++11)
  • xvalue
  • class prvalue
  • function lvalue
(desde C++11)
(hasta C++17)
  • rvalue
  • function lvalue
(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 P .

  • P se ajusta a partir del tipo del resultado de la conversión añadiendo la calificación cv de T a él.

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 T y se inicializa por copia desde target . La referencia se enlaza entonces al temporal.

(until C++17)

De lo contrario, target se convierte implícitamente a un prvalue de tipo "cv-unqualified T ". Se aplica la conversión de materialización temporal, considerando el tipo del prvalue como T , y la referencia se enlaza al objeto resultante.

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

Existen las siguientes excepciones a esta regla de duración:

  • un temporal vinculado a un valor de retorno de una función en una return statement no se extiende: se destruye inmediatamente al final de la expresión de retorno. Tal return statement siempre retorna una referencia colgante.
(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.
  • un temporal vinculado a una referencia en el inicializador usado en una expresión new existe hasta el final de la expresión completa que contiene esa expresión new, no tanto como el objeto inicializado. Si el objeto inicializado sobrevive a la expresión completa, su miembro de referencia 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

Véase también