c++ - ¿Los valores de retorno de la función son objetos automáticos y, por lo tanto, se garantiza que se destruirán?
exception-handling return (3)
Esto es un error, y por una vez, MSVC realmente lo hace bien: imprime "~ A".
En [except.ctor] la norma ( N4140 ) garantiza que:
... se invocan destructores para todos los objetos automáticos construidos desde que se ingresó el bloque de prueba ...
Sin embargo, en el siguiente ejemplo, la
output
vacía prueba que el valor de retorno de la función
foo
no se destruye, aunque se ha construido.
Compilado usando g ++ (5.2.1) y clang ++ (3.6.2-1) y con las opciones
-O0 -fno-elide-constructors -std=c++14
.
struct A { ~A() { cout << "~A/n"; } };
struct B { ~B() noexcept(false) { throw 0; } };
A foo() {
B b;
return {};
}
int main() {
try { foo(); }
catch (...) { }
}
¿Es esto un error tanto en g ++ como en clang ++, o los valores de retorno de la función no se consideran objetos automáticos, o es un agujero de bucle en el lenguaje C ++?
En ninguno de [stmt.return], [expr.call] o [dcl.fct] he podido encontrar una declaración clara de si el valor de retorno de una función se considera un objeto automático. Los consejos más cercanos que encontré son 6.3.3 p2:
... Una declaración de devolución puede involucrar la construcción y copia o movimiento de un objeto temporal ...
y 5.2.2 p10:
Una llamada a la función es un valor l si el tipo de resultado es un tipo de referencia lvalue o una referencia rvalue al tipo de función, un valor x si el tipo de resultado es una referencia rvalue al tipo de objeto y un prvalue en caso contrario.
Los valores de retorno de la función se consideran temporales, y la construcción del valor de retorno se secuencia antes de la destrucción de los locales.
Desafortunadamente, esto no está especificado en el estándar. Hay un defecto abierto que describe esto y ofrece algunas palabras para solucionar el problema.
[...] Una declaración de retorno con un operando de tipo void solo se utilizará en una función cuyo tipo de retorno sea cv void. Una declaración de retorno con cualquier otro operando se usará solo en una función cuyo tipo de retorno no sea cv void; la declaración return inicializa el objeto o la referencia que se devolverá mediante la inicialización de copia (8.5 [dcl.init]) desde el operando. [...]
La inicialización de la copia de la entidad devuelta se secuencia antes de la destrucción de los temporales al final de la expresión completa establecida por el operando de la declaración de retorno, que, a su vez, se secuencia antes de la destrucción de las variables locales (6.6 [stmt. jump]) del bloque que encierra la declaración return.
Dado que los valores de retorno de funciones son temporales, no están cubiertos por los
destructors are invoked for all automatic objects
comillas al comienzo de su publicación.
Sin embargo,
[class.temporary]/3
dice:
[...] Los objetos temporales se destruyen como el último paso para evaluar la expresión completa que (léxicamente) contiene el punto donde fueron creados. Esto es cierto incluso si esa evaluación termina arrojando una excepción . [...]
Así que creo que podrías considerar esto un error en GCC y Clang.
No tirar de los destructores;)
Modifiqué su código y creo que ahora desde el resultado podemos ver que A no se destruye.
#include<iostream>
using namespace std;
struct A {
~A() { cout << "~A/n"; }
A() { cout << "A()"; }
};
struct B {
~B() noexcept( false ) { cout << "~B/n"; throw(0); }
B() { cout << "B()"; }
};
A foo() {
B b;
return;
}
int main() {
try { foo(); }
catch (...) {}
}
Y la salida es:
B () A () ~ B
Entonces sí, podría ser un error.