c++ - usar - ¿Cuál es el orden de destrucción de los argumentos de función?
parametros por linea de comandos c (3)
Si alguna función f
con los parámetros p_1
, ..., p_n
de los tipos T_1
, ..., T_n
respectivamente se llama con los argumentos a_1
, ..., a_n
y su cuerpo arroja una excepción, termina o regresa, en qué orden están los argumentos destruidos y por qué? Proporcione una referencia al estándar, si es posible.
EDITAR: En realidad, quería preguntar sobre los "parámetros" de la función, pero cuando TC y Columbo lograron aclarar mi confusión, dejo esta pregunta sobre los argumentos y formulé una nueva pregunta por separado sobre los parámetros . Ver los comentarios sobre esta pregunta para la distinción.
El orden en que se evalúan los argumentos para una función no está especificado por el estándar. Del Estándar C ++ 11 ( borrador en línea ):
5.2.2 Llamada de función
8 [ Nota: las evaluaciones de la expresión de postfijo y de las expresiones de argumento no son secuenciadas una respecto de la otra. Todos los efectos secundarios de las evaluaciones de expresiones de argumentos se ordenan antes de ingresar la función (ver 1.9). -Finalizar nota ]
Por lo tanto, depende completamente de una implementación decidir en qué orden evaluar los argumentos para una función. Esto, a su vez, implica que el orden de construcción de los argumentos también depende de la implementación.
Una implementación sensata destruiría los objetos en el orden inverso de su construcción.
En §5.2.2 [4] N3337 es bastante explícito sobre lo que sucede ( borrador en línea ):
Durante la inicialización de un parámetro, una implementación puede evitar la construcción de temporales adicionales combinando las conversiones en el argumento asociado y / o la construcción de temporales con la inicialización del parámetro (véase 12.2). La duración de un parámetro finaliza cuando la función en la que se define regresa.
Entonces, por ejemplo, en
f(g(h()));
el valor de retorno de la llamada h()
es un temporal que se destruirá al final de la expresión completa. Sin embargo, el compilador puede evitar esto temporalmente e inicializar directamente con su valor el parámetro de g()
. En este caso, el valor de retorno se destruirá una vez que g()
regrese (es decir, ANTES de llamar a f()
).
Si entendí correctamente lo que se establece en la norma, no se permite que el valor devuelto por h()
sobreviva hasta el final de la expresión completa a menos que se haga una copia (el parámetro) y esta copia se destruya una vez g()
devoluciones.
Los dos escenarios son:
-
h
valor de retornoh
se usa para inicializar directamente el parámetrog
. Este objeto se destruye cuandog
vuelve y antes de llamar af
. -
h
valor de retorno es temporal. Se realiza una copia para inicializar el parámetrog
y se destruye cuandog
vuelve. El temporal original se destruye al final de la expresión completa en su lugar.
No sé si las implementaciones siguen las reglas sobre esto.
No pude encontrar la respuesta en el estándar, pero pude probar esto en los 3 compiladores más populares que cumplen con C ++. La respuesta de R Sahu explica que se trata de una implementación definida.
§5.2.2 / 8 : las evaluaciones de la expresión de postfijo y de los argumentos no son secuenciadas una respecto de la otra. Todos los efectos secundarios de las evaluaciones de argumentos se ordenan antes de ingresar la función.
Compilador de Visual Studio C ++ (Windows) y gcc (Debian)
Los argumentos se construyen en orden inverso a su declaración y se destruyen en orden inverso (así se destruye en orden de decalación):
2
1
-1
-2
Clang (FreeBSD)
Los argumentos se construyen por orden de su declaración y se destruyen en orden inverso:
1
2
-2
-1
Todos los compiladores fueron instruidos para tratar el código fuente como C ++ 11 y utilicé el siguiente fragmento para demostrar la situación:
struct A
{
A(int) { std::cout << "1" << std::endl; }
~A() { std::cout << "-1" << std::endl; }
};
struct B
{
B(double) { std::cout << "2" << std::endl; }
~B() { std::cout << "-2" << std::endl; }
};
void f(A, B) { }
int main()
{
f(4, 5.);
}