vida util solidos rpvot lubricantes los hidraulico grasas fase caracteristicas almacenamiento aceites aceite c++ reference lifetime

c++ - util - ¿Se extiende la vida útil de una referencia?



vida util de los aceites lubricantes (3)

Me gustaría pasar una referencia a una función. Este código no funciona, como era de esperar:

struct A { }; void foo(A& a) { // do something with a } int main(int, char**) { foo(A()); }

Obtengo el error de compilación

Inicialización inválida de referencia no constante de tipo A& de un valor r de tipo A

Pero cuando simplemente agrego el método A& ref() a A como a continuación y lo llamo antes de pasarlo, parece que puedo usar a . Cuando se depura, el objeto A se destruye después de llamar a foo() :

struct A { A& ref() { return *this; } }; void foo(A& a) { // do something with a } int main(int, char**) { foo(A().ref()); }

¿Es este código válido de acuerdo con el estándar? ¿Llamar ref() extiende mágicamente la vida útil del objeto hasta que devuelve foo() ?


Hay varias preguntas en esta pregunta. Intentaré abordarlos a todos:

En primer lugar, no puede pasar un valor temporal (prvalue) de tipo A a una función que toma A& porque las referencias de valor l no const no pueden vincularse a valores r. Esa es una restricción de idioma. Si desea poder pasar un temporal, o bien necesita tomar un parámetro de tipo A&& o de tipo A const& - el último ya que los temporales pueden vincularse a las referencias de const lvalue.

¿Es este código válido de acuerdo con el estándar? ¿Llamar ref() extiende mágicamente la vida útil del objeto hasta que devuelve foo() ?

No hay extensión de por vida en su programa en absoluto. De [class.temp]:

Hay tres contextos en los que los temporales se destruyen en un punto diferente al final de la expresión completa. El primer contexto es cuando se invoca un constructor predeterminado para inicializar un elemento de una matriz sin el inicializador correspondiente (8.6). El segundo contexto es cuando se llama a un constructor de copia para copiar un elemento de una matriz mientras se copia toda la matriz (5.1.5, 12.8). [...] El tercer contexto es cuando una referencia está vinculada a un temporal.

Ninguno de esos contextos se aplica. Nunca estamos vinculando una referencia a un temporal en este código. ref() enlaza *this con una A& , pero *this no es temporal, y luego esa referencia resultante simplemente pasa a foo() .

Considera esta variante de tu programa:

#include <iostream> struct A { A& ref() { return *this; } ~A() { std::cout << "~A()/n"; } }; int main() { auto& foo = A().ref(); std::cout << "----/n"; }

que imprime

~A() ----

que ilustra que no hay extensión de por vida.

Si en lugar de vincular el resultado de ref() a una referencia en su lugar vinculamos un miembro:

#include <iostream> struct A { A& ref() { return *this; } int x; ~A() { std::cout << "~A()/n"; } }; int main() { auto&& foo = A().x; std::cout << "----/n"; }

entonces en realidad estamos vinculando un temporal a una referencia y se aplica ese tercer contexto: la vida útil del objeto completo del subobjeto al que se vincula la referencia se mantiene durante el tiempo de vida de la referencia. Entonces este código se imprime:

---- ~A()


Tu código es perfectamente válido.

En esta linea

foo(A().ref());

La instancia de una A temporal vive hasta el final de la declaración ( ; ).

Es por eso que es seguro pasar A& regresar de ref() a foo (siempre que foo no lo almacene).

ref() por sí solo no extiende ninguna duración, pero ayuda al devolver una referencia lvalue.

¿Qué sucede en el caso de foo(A()); ? Aquí el temporal se pasa como un valor r. Y en C ++, un valor r no se une a las referencias de valor l no const (incluso en C ++ 11, una referencia rvalue no se une a las referencias lvalue no constantes).

De este artículo del blog de Visual C ++ sobre las referencias de rvalue :

... C ++ no quiere que modifiques temporalmente las temporarias, pero llamar directamente a una función miembro no const en un valor r modificable es explícito, por lo que está permitido ...


A() crea un objeto temporal de tipo A El objeto existe hasta el final de la expresión completa en la que se creó. El problema en su código original no es la duración de este temporal; es que la función toma su argumento como una referencia no constante, y no se permite pasar un objeto temporal como referencia no constante. El cambio más simple es que foo tome su argumento por referencia constante, si es apropiado para lo que hace la función:

void foo(const A&); int main() { foo(A()); }