Espacios de nombres
Variantes
Acciones

Inicialización de referencia

De cppreference.com
< cpp‎ | language

Vincula una referencia a un objeto

Contenido

[editar] Sintaxis

T & ref = objeto ;

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

T & ref ( objeto ) ;

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

(1)

T && ref = objeto ;

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

T && ref ( objeto ) ;

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

(2) (desde C++11)
Dado R fn ( T & arg ); o R fn ( T && arg );

fn ( objeto )

fn ( { arg1, arg2, ... } )

(3)
Dentro de T & fn () o T && fn ()

return objeto ;

(4)
Dado T & ref ; o T && ref ; dentro de la definición de Clase

Clase::Clase(...) : ref( objeto) {...}

(5)

[editar] Explicación

Una referencia a T se puede inicializar 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 se puede cambiar para hacer referencia a otro objeto.

Las referencias se inicializan en las siguientes situaciones:

1) Cuando una variable referencia lvalue denominada se declara con un inicializador.
2) Cuando una variable referencia rvalue denominada se declara con un inicializador.
3) En una expresión de llamada a función, cuando el parámetro de la función tiene un tipo referencia.
4) En la instrucción return, cuando la función devuelve un tipo referencia.
5) Cuando un dato miembro no estático de tipo referencia se inicializa usando un inicializador de miembro.

Los efectos de la inicialización de referencia son:

  • Si el inicializador es una lista de inicializadores entre llaves { arg1, arg2, ... } , se siguen las reglas de la inicialización de lista.
  • De lo contrario, si la referencia es una referencia lvalue:
  • Si objeto es una expresión lvalue, y su tipo es T o derivado de T, y es igual o menos calificado-cv, entonces la referencia se vincula al objeto identificado por el lvalue o al subobjeto de su clase base.
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 el tipo de objeto no es el mismo o se deriva de T, y T tiene una función de conversión a un lvalue cuyo tipo es ya sea T o derivado de T, igual o menos calificado-cv, entonces la referencia se vincula al objeto identificado por el lvalue devuelto por la función de conversión (o el subobjeto de su clase base).
struct A {};
struct B : A { operator int&(); };
int& ir = B(); // ir se refiere al resultado de B::operator int&
  • De lo contrario, si la referencia es una referencia lvalue a un tipo no volátil calificado const o referencia rvalue (desde C++11):
  • Si objeto es un rvalue que no es un campo de bits o un lvalue de función, y su tipo es ya sea T o derivado de T, igual o menos calificado-cv, entonces la referencia se vincula al valor de la expresión de inicializador o a su subobjeto base (después de materializar un temporal si es necesario) (desde C++17).
struct A {};
struct B : A {};
extern B f();
const A& rca2 = f(); // vinculado al subobjeto A del rvalue B.
A&& rra = f();       // lo mismo que arriba
 
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // vinculado directamente a i2
  • De lo contrario, si el tipo de objeto no es el mismo o derivado de T, y objeto tiene una función de conversión a un rvalue o un lvalue de función cuyo tipo es ya sea T o derivado de T, igual o menos calificado-cv, entonces la referencia se vincula al resultado de la función de conversión o al subobjeto de su clase base (después de materializar un temporal si es necesario) (desde C++17).
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
  • De lo contrario, objeto se convierte implícitamente a T. La referencia se vincula al resultado de la conversión (después de materializar un temporal) (desde C++17). Si el objeto (o si la conversión se hace mediante una conversión definida por el usuario, el resultado de la función de conversión) es de tipo T o derivado de T, debe ser igual o menos calificado-cv que T, y, si la referencia es una referencia rvalue, no debe ser un lvalue (desde C++11).
const std::string& rs = "abc"; // rs se refiere al temporal inicializado
                               // por inicialización de copia de char array
const double& rcd2 = 2;        // rcd2 se refiere al temporal con value 2.0
int i3 = 2;
double&& rrd3 = i3;            // rrd3 se refiere al temporal con valor 2.0

[editar] Tiempo de vida de un temporal

Siempre que una referencia se vincula a un temporal o a un subobjeto base de un temporal, el tiempo de vida del temporal se extiende para que coincida con el tiempo de vida de la referencia, con las siguientes excepciones:

  • Un temporal vinculado a un valor de retorno de una función en una instrucción return no se extiende: se destruye inmediatamente al final de la expresión de retorno. Esta función siempre devuelve una referencia pendiente.
  • Un temporal vinculado a una referencia miembro en un constructor que usa una lista de inicializadores persiste solamente hasta que el constructor egrese, no tanto como el objeto exista (observa que tal inicialización está mal formada a partir del informe de defecto DR 1696).
(hasta C++14)
  • Un temporal vinculado a un parámetro de referencia en una llamada a función existe hasta el final de la expresión completa que contiene dicha llamada a función: si la función devuelve una referencia que vive más allá de la expresión completa, se convierte en una referencia pendiente.
  • Un temporal vinculado a una referencia en el inicializador utilizado 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 vive más allá de la expresión completa, su referencia miembro se convierte en una referencia pendiente.
(desde C++11)
  • Un temporal vinculado a una referencia en un elemento de tipo referencia de un agregado inicializado usando la sintaxis de inicialización directa (paréntesis), en lugar de la sintaxis de inicialización de lista (llaves) existe hasta el final de la expresión completa que contiene el inicializador.
struct A {
  int&& r;
};
A a1{7}; // de acuerdo, el tiempo de vida se extiende
A a2(7); // bien formado, pero es una referencia pendiente
(desde C++20)

En general, el tiempo de vida de un temporal no puede extenderse "al pasarlo": una segunda referencia, inicializada a partir de la referencia a la que el temporal estaba vinculado, no afecta el tiempo de vida del temporal.

[editar] Notas

Las referencias aparecen sin inicializadores solamente 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 especificador extern.

[editar] Ejemplo

#include <utility>
#include <sstream>
struct S {
 int mi;
 const std::pair<int,int>& mp; // referencia miembro
};
 
void foo(int) {}
 
struct A {};
struct B : A {
   int n;
   operator int&() { return n; };
};
 
B bar() {return B(); }
 
//int& bad_r; // ERROR: no hay inicializador
extern int& ext_r; // de acuerdo
 
int main()
{
 // lvalues
    int n = 1;
    int& r1 = n;  // referencia lvalue al objeto n
    const int& cr(n); // la referencia puede ser más calificada-cv
    volatile int& cv{n}; // puede usarse cualquier sintaxis de inicializador
    int& r2 = r1; // otra referencia lvalue al objeto n
//    int& bad = cr; // ERROR: menos calificada-cv
    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 a subobjeto base
    int& converted_ref = b; // referencia al resultado de una conversión
 
// rvalues
//  int& bad = 1; // ERROR: no se puede vincular ref lvalue a rvalue
    const int& cref = 1; // vinculado a rvalue
    int&& rref = 1; // vinculado rvalue
 
    const A& cref2 = bar(); // referencia a subobjeto A del temporal B
    A&& rref2 = bar();      // lo mismo
 
    int&& xref = static_cast<int&&>(n); // vincular directamente a n
//  int&& copy_ref = n; // ERROR: no se puede vincular a un lvalue
    double&& copy_ref = n; // vincular a un rvalue temporal con valor 1.0
 
// restrictions on temporary lifetimes
    std::ostream& buf_ref = std::ostringstream() << 'a'; // el temporal ostringstream
                      // se vinculó al operando al operando izquierdo de operator<<,
                      // pero su duración terminó en el punto y coma:
                      // buf_ref ahora es una referencia pendiente.
 
    S a { 1, {2,3} }; // par temporal {2,3} vinculado a la referencia miembro
                      // a.mp y su duración se extendieron para coincidir con un
    S* p = new S{ 1, {2,3} }; // par temporal {2,3} vinculado a la referencia
                      // miembro a.mp, pero su duración terminó en el punto y coma
                      //  p->mp es una referencia pendiente
    delete p;
}


[editar] Véase también