c++ - queries - mysql recomendaciones



¿Por qué la duración de la vida temporal no se extiende hasta la duración de la vida del objeto cerrado? (5)

Sé que un temporal no puede vincularse a una referencia no constante, pero puede vincularse a una referencia constante. Es decir,

 A & x = A(); //error
 const A & y = A(); //ok

También sé que en el segundo caso (arriba), la vida útil del temporal creado a partir de A() extiende hasta la vida útil de la referencia constante (es decir, y ).

Pero mi pregunta es:

¿Puede la referencia constante que está vinculada a un temporal, estar más vinculada a otra referencia constante, extendiendo la vida útil del temporal hasta la vida útil del segundo objeto?

Intenté esto y no funcionó. No entiendo exactamente esto. Escribí este código:

struct A
{
   A()  { std::cout << " A()" << std::endl; }
   ~A() { std::cout << "~A()" << std::endl; }
};

struct B
{
   const A & a;
   B(const A & a) : a(a) { std::cout << " B()" << std::endl; }
   ~B() { std::cout << "~B()" << std::endl; }
};

int main() 
{
        {
            A a;
            B b(a);
        }
        std::cout << "-----" << std::endl;
        {
            B b((A())); //extra braces are needed!
        }
}

Salida ( ideone ):

 A()
 B()
~B()
~A()
-----
 A()
 B()
~A()
~B()

¿Diferencia en la salida? ¿Por qué el objeto temporal A() se destruye antes que el objeto b en el segundo caso? ¿El estándar (C ++ 03) habla sobre este comportamiento?


§12.2 / 5 dice

Un enlace temporal a un parámetro de referencia en una llamada de función (5.2.2) persiste hasta la finalización de la expresión completa que contiene la llamada.

Bastante cortado y seco, de verdad.


En su primera ejecución, los objetos se destruyen en el orden en que fueron empujados en la pila - que es empujar A, empujar B, pop B, pop A.

En la segunda carrera, la vida de A termina con la construcción de b. Por lo tanto, crea A, crea B desde A, la vida útil de A finaliza para que se destruya, y luego B se destruye. Tiene sentido...


La sección 12.2 / 5 dice: "El segundo contexto [cuando se extiende la vida útil de un temporal] es cuando una referencia está vinculada a una temporal". Tomado literalmente, esto indica claramente que la vida útil debe extenderse en su caso; tu B::a está ciertamente ligado a un temporal. (Una referencia se enlaza con un objeto, y no veo ningún otro objeto al que pueda estar vinculado). Sin embargo, esta es una redacción muy pobre; Estoy seguro de que lo que se quiere decir es "El segundo contexto es cuando se usa un temporal para inicializar una referencia", y el tiempo de vida extendido corresponde al de la referencia iniciada con la expresión rvalue creando el temporal, y no al de ningún otro. Otras referencias que luego pueden ser vinculadas al objeto. En su forma actual, la redacción requiere algo que simplemente no es implementable: considere:

void f(A const& a)
{
    static A const& localA = a;
}

llamado con:

f(A());

¿Dónde debe colocar el compilador A() (dado que generalmente no puede ver el código de f() , y no conoce la estática local cuando genera la llamada)?

Creo, en realidad, que esto vale un DR.

Podría agregar que hay un texto que sugiere fuertemente que mi interpretación de la intención es correcta. Imagina que tienes un segundo constructor para B :

B::B() : a(A()) {}

En este caso, B::a se inicializaría directamente con un temporal; la vida de este temporal debería extenderse incluso por mi interpretación. Sin embargo, la norma hace una excepción específica para este caso; tal temporal solo persiste hasta que el constructor sale (lo que de nuevo lo dejaría con una referencia pendiente). Esta excepción proporciona una indicación muy clara de que los autores de la norma no pretendían que las referencias de los miembros en una clase extendieran la vida útil de los temporales a los que están obligados; De nuevo, la motivación es la implementabilidad. Imagina que en lugar de

B b((A()));

tu habias escrito

B* b = new B(A());

¿Dónde debería colocar el compilador el A() temporal A() para que su vida útil sea la del B asignado dinámicamente?


No conozco los estándares, pero puedo discutir algunos hechos que vi en algunas preguntas anteriores.

La primera salida es tal como es por razones obvias de que a y b están en el mismo ámbito. También se destruye a después de b porque se construyó antes b .

Supongo que debería estar más interesado en la segunda salida. Antes de comenzar, debemos tener en cuenta que los siguientes tipos de creaciones de objetos (temporales independientes):

{
  A();
}

durar solo hasta el siguiente ; Y no por el bloque que lo rodea . Demo En tu segundo caso, cuando lo hagas,

B b((A()));

por lo tanto, A() se destruye tan pronto como finaliza la creación del objeto B() . Dado que, la referencia constante puede vincularse a temporal, esto no generará un error de compilación. Sin embargo, seguramente resultará en un error lógico si intentas acceder a B::a , que ahora está vinculado a una variable fuera de alcance.


Tu ejemplo no realiza una extensión de vida anidada

En el constructor

B(const A & a_) : a(a_) { std::cout << " B()" << std::endl; }

El a_ aquí (renombrado para exposición) no es temporal. Si una expresión es temporal es una propiedad sintáctica de la expresión y una expresión-id nunca es temporal. Así que no se produce ninguna extensión de por vida aquí.

Aquí hay un caso en el que se produciría una extensión de por vida:

B() : a(A()) { std::cout << " B()" << std::endl; }

Sin embargo, debido a que la referencia se inicializa en un inicializador ctor, la vida útil solo se extiende hasta el final de la función. Por [class.temporary] p5 :

Un enlace temporal a un miembro de referencia en un inicializador ctor (12.6.2) de un constructor persiste hasta que el constructor sale.

En la convocatoria al constructor.

B b((A())); //extra braces are needed!

Aquí, estamos vinculando una referencia a un temporal. [class.temporary] p5 dice:

Un enlace temporal a un parámetro de referencia en una llamada de función (5.2.2) persiste hasta la finalización de la expresión completa que contiene la llamada.

Por lo tanto, el A temporal se destruye al final de la declaración. Esto sucede antes de que la variable B se destruya al final del bloque, lo que explica su salida de registro.

Otros casos hacen extensión de vida anidada

Inicialización de variables agregadas

La inicialización agregada de una estructura con un miembro de referencia puede prolongar toda la vida:

struct X {
  const A &a;
};
X x = { A() };

En este caso, el A temporal está enlazado directamente a una referencia, por lo que el temporal se extiende a la vida útil de xa , que es igual a la vida útil de x . (Advertencia: hasta hace poco, muy pocos compiladores lo entendían bien).

Agregación temporal de inicialización

En C ++ 11, puede usar la inicialización agregada para inicializar un temporal, y así obtener la extensión de vida recursiva:

struct A {
   A()  { std::cout << " A()" << std::endl; }
   ~A() { std::cout << "~A()" << std::endl; }
};

struct B {
   const A &a;
   ~B() { std::cout << "~B()" << std::endl; }
};

int main() {
  const B &b = B { A() };
  std::cout << "-----" << std::endl;
}

Con el tronco Clang o g ++, esto produce el siguiente resultado:

 A()
-----
~B()
~A()

Tenga en cuenta que tanto la A temporal como la B temporal tienen una vida útil prolongada. Debido a que la construcción del A temporal se completa primero, se destruye en último lugar.

In std::initializer_list<T> inicialización

std::initializer_list<T> C ++ 11 realiza la extensión de por vida como si vinculara una referencia a la matriz subyacente. Por lo tanto, podemos realizar una extensión de vida útil anidada usando std::initializer_list . Sin embargo, los errores del compilador son comunes en esta área:

struct C {
  std::initializer_list<B> b;
  ~C() { std::cout << "~C()" << std::endl; }
};
int main() {
  const C &c = C{ { { A() }, { A() } } };
  std::cout << "-----" << std::endl;
}

Produce con tronco Clang:

 A()
 A()
-----
~C()
~B()
~B()
~A()
~A()

y con g ++ tronco:

 A()
 A()
~A()
~A()
-----
~C()
~B()
~B() 

Estos son ambos incorrectos; la salida correcta es:

 A()
 A()
-----
~C()
~B()
~A()
~B()
~A()




temporary