¿El estándar C++ garantiza que la inicialización uniforme es a prueba de excepciones?
exception gcc (1)
#include <iostream>
using namespace std;
struct A
{
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }
};
A Ok() { return {}; }
A NotOk() { throw "NotOk"; }
struct B
{
A a1;
A a2;
};
void f(B) {}
int main()
{
try
{
f({ Ok(), NotOk() });
}
catch (...)
{}
}
vc++
y salida clang
:
A
~A
Mientras que las salidas de gcc
:
A
Parece un error grave de GCC.
Como referencia, vea el error 66139 de GCC y "Un error grave en GCC" por Andrzej Krzemieński .
Solo me pregunto:
¿El estándar C ++ garantiza que la inicialización uniforme es a prueba de excepciones?
Así parece:
Curiosamente encontrado en §6.6 / 2 Jump Statements [stmt.jump] de todos los lugares (N4618):
Al salir de un ámbito (independientemente de cómo se haya realizado), los objetos con duración de almacenamiento automático (3.7.3) que se han construido en ese ámbito se destruyen en el orden inverso al de su construcción. [Nota: para los temporales, vea 12.2. -finalizar] La transferencia de un bucle, de un bloque o de una variable inicializada con una duración de almacenamiento automática implica la destrucción de objetos con duración de almacenamiento automático que están en el alcance en el punto transferido desde, pero no en el punto transferido a . (Ver 6.7 para transferencias en bloques). [Nota: Sin embargo, el programa puede finalizar (llamando a
std::exit()
ostd::abort()
(18.5), por ejemplo) sin destruir objetos de clase con duración de almacenamiento automático. -finalizar nota]
Creo que el énfasis aquí está en la parte "(sin embargo)". Esto incluye una excepción (pero excluye cosas que causan un std::terminate
).
EDITAR
Creo que una mejor referencia es §15.2 / 3 Constructores y destructores [excepto.ctor] ( énfasis mío):
Si la inicialización o destrucción de un objeto que no sea delegando el constructor finaliza con una excepción, se invoca el destructor para cada uno de los subobjetos directos del objeto y, para un objeto completo, los subobjetos de clase de base virtual, cuya inicialización se ha completado (8.6) y cuyo destructor aún no ha comenzado la ejecución, excepto que en el caso de la destrucción, los miembros variantes de una clase sindical no se destruyen. Los subobjetos se destruyen en el orden inverso de la finalización de su construcción. Dicha destrucción se secuencia antes de entrar en un controlador de la función-try-block del constructor o destructor, si lo hay.
Esto incluiría la inicialización agregada (que aprendí hoy se puede llamar inicialización no vacía )
... y para objetos con constructores podemos citar §12.6.2 / 12 [class.base.init] ( énfasis mío):
En un constructor no delegante, el destructor para cada subobjeto potencialmente construido de tipo de clase se invoca potencialmente (12.4). [Nota: esta disposición garantiza que los destructores puedan ser llamados para subobjetos totalmente construidos en caso de que se genere una excepción (15.2). -finalizar nota]