c++ c++11 const undefined-behavior

c++ - Está capturando un objeto recién construido por const ref comportamiento indefinido



c++11 undefined-behavior (5)

¿Está bien el siguiente (ejemplo artificial) o es un comportamiento indefinido:

// undefined behavior? const auto& c = SomeClass{}; // use c in code later const auto& v = c.GetSomeVariable();


Es seguro en este caso específico. Sin embargo, tenga en cuenta que no todos los temporales son seguros de capturar por referencia constante ... por ejemplo

#include <stdio.h> struct Foo { int member; Foo() : member(0) { printf("Constructor/n"); } ~Foo() { printf("Destructor/n"); } const Foo& method() const { return *this; } }; int main() { { const Foo& x = Foo{}; // safe printf("here!/n"); } { const int& y = Foo{}.member; // safe too (special rule for this) printf("here (2)!/n"); } { const Foo& z = Foo{}.method(); // NOT safe printf("here (3)!/n"); } return 0; }

La referencia obtenida para z NO es segura de usar porque la instancia temporal se destruirá al final de la expresión completa, antes de llegar a la declaración printf . Salida es:

Constructor here! Destructor Constructor here (2)! Destructor Constructor Destructor here (3)!


Es seguro. Const ref prolonga la vida de temporal. El alcance será el alcance de const ref.

La lifetime de un objeto temporal puede extenderse uniéndose a una referencia de valor constante o a una referencia de valor (desde C ++ 11), consulte la inicialización de referencia para obtener más detalles.

Siempre que una referencia esté vinculada a un objeto temporal o subobjeto del mismo, la vida útil del temporal se extiende para que coincida con la vida útil de la referencia, con las siguientes excepciones :

  • un enlace temporal a un valor de retorno de una función en una declaración de retorno no se extiende: se destruye inmediatamente al final de la expresión de retorno. Dicha función siempre devuelve una referencia colgante.
  • un enlace temporal a un miembro de referencia en una lista de inicializador de constructor persiste solo hasta que el constructor sale, no mientras exista el objeto. (nota: dicha inicialización está mal formada a partir del DR 1696).
  • existe un enlace temporal a un parámetro de referencia en una llamada de función hasta el final de la expresión completa que contiene esa llamada de función: si la función devuelve una referencia, que sobrevive a la expresión completa, se convierte en una referencia colgante.
  • existe un enlace temporal a una referencia en el inicializador utilizado en una nueva expresión hasta el final de la expresión completa que contiene esa nueva expresión, 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.
  • existe un enlace temporal a una referencia en un elemento de referencia de un agregado inicializado utilizando la sintaxis de inicialización directa (paréntesis) en lugar de la sintaxis de inicialización de lista (llaves) hasta el final de la expresión completa que contiene el inicializador. struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

En general, la vida útil de un temporal no puede extenderse aún más "pasándola": una segunda referencia, inicializada a partir de la referencia a la que estaba vinculado el temporal, no afecta su vida útil.

como señaló @Konrad Rudolph (y vea el último párrafo de arriba):

"Si c.GetSomeVariable() devuelve una referencia a un objeto local o una referencia de que está extendiendo la vida útil de algún objeto, la extensión de la vida útil no se activa"


No debería haber ningún problema aquí, gracias a la extensión de por vida . El objeto recién construido sobrevivirá hasta que la referencia salga del alcance.


Sí, esto es perfectamente seguro: el enlace a una referencia const extiende la vida útil de lo temporal al alcance de esa referencia.

Sin embargo, tenga en cuenta que el comportamiento no es transitivo . Por ejemplo, con

const auto& cc = []{ const auto& c = SomeClass{}; return c; }();

cc cuelga.


Esto es seguro

[class.temporary]/5 : Hay tres contextos en los que los temporales se destruyen en un punto diferente al final de la expresión completa . [..]

[class.temporary]/6 : El tercer contexto es cuando una referencia está vinculada a un objeto temporal. El objeto temporal al que está vinculada la referencia o el objeto temporal que es el objeto completo de un subobjeto al que está vinculada la referencia persiste durante toda la vida útil de la referencia si el valor gl al que está vinculada la referencia se obtuvo a través de uno de los siguientes : [muchas cosas aquí]