¿Se garantiza que el destructor de un objeto local dentro de un bucle se invocará antes de la próxima iteración?

11

Cuando tengo un bucle y dentro de este bucle, creo una nueva variable de pila (sin asignarla en el montón y la variable que la mantiene declarada dentro del cuerpo del bucle), ¿se garantiza que se llamará al destructor de este objeto antes de que comience la próxima iteración, o podría ¿Desenrollar el bucle por el compilador cambia algo al respecto?

usuario1282931
fuente
1
El desenrollamiento de bucle per se no cambiará el orden de ejecución. Sin embargo, la paralelización de bucles podría hacer esto.
Adrian Mole

Respuestas:

8

De n4800:

§6.3.3 Alcance del bloque :

Un nombre declarado en un bloque (8.3) es local para ese bloque; Tiene alcance de bloque. Su alcance potencial comienza en su punto de declaración (6.3.2) y termina al final de su bloque. Una variable declarada en el alcance del bloque es una variable local.

§10.3.6 Destructores :

Un destructor se invoca implícitamente [...] cuando el bloque en el que se crea un objeto sale (8.7)

§4.1.1 Máquina abstracta :

Esta disposición a veces se llama la regla "como si", porque una implementación es libre de ignorar cualquier requisito de este documento siempre que el resultado sea como si el requisito hubiera sido obedecido, en la medida en que pueda determinarse a partir del comportamiento observable de El programa .

[El énfasis es mío]

Entonces sí. Su variable queda fuera de alcance al final del ciclo (que es un bloque) y, por lo tanto, se llama a su destructor por lo que cualquiera que observe el comportamiento del programa puede ver .

Paul Evans
fuente
1
No hay nada allí cuando se llama al destructor.
rígido
2
@stark Lo que les permite hacer esto es la regla as-if. El estándar solo especifica el comportamiento de la máquina abstracta. No estoy seguro de si es necesario entrar en todos esos detalles en las respuestas aquí.
Max Langhof
2
@stark Esto es, en mi opinión, irrelevante para la pregunta. También puede decir que los destructores pueden estar en línea y, por lo tanto, no calleditados. O, si efectivamente (como regla if) no hacen nada, puede que no se genere un ensamblaje para dichos destructores.
Daniel Langr
2
@stark Vea ¿Qué es exactamente la regla "como si"? .
Daniel Langr
2
@stark ¿Dónde se define qué ? Tenga en cuenta que esta discusión está fuera del tema de la pregunta. Puede hacer otra pregunta por separado sobre este problema.
Daniel Langr
8

Si. Es más fácil visualizar cuando considera los "bloques" en los que declara una variable, es decir, entre qué par de llaves. El ciclo es un bloque en sí mismo, y cuando alcanza el paréntesis de cierre, antes de la siguiente iteración, se llama a todos los destructores de variables de almacenamiento automático declaradas en el ciclo.

¿podría el bucle desenrollar por el compilador cambiar algo al respecto?

Como regla general, no piense en lo que optimizará el compilador, porque aún necesita garantizar el comportamiento de su programa, sin importar lo que haga para optimizarlo. En ese caso, el desenrollado de bucle no cambiará nada a ese efecto si sucede.

JBL
fuente
2
+1 para la regla general, al escribir código uno no debe preocuparse por los componentes internos del compilador. Iba a agregar algo con el mismo espíritu a mi respuesta, pero ahora ya está allí
idclev 463035818
copy-elision y RVO cambian el comportamiento del programa, ¿no es así?
Jean-Baptiste Yunès
@ Jean-BaptisteYunès potencialmente pueden, sin embargo, el estándar les permite también por[class.copy.elision]
ChrisMM
No solo un par de llaves . Puede escribir for(...) X x{};y el xobjeto se construirá + destruirá en cada iteración. Demostración en vivo . Una sección estándar relevante es stmt.iter / 2 .
Daniel Langr
@DanielsaysreinstateMonica Según §9.5.2 [stmt.iter]es puramente equivalente (énfasis mío): "Si la declaración en una declaración de iteración es una declaración única y no una declaración compuesta, es como si se hubiera reescrito para ser una declaración compuesta que contiene la declaración original ". En esencia, con o sin llaves para una sola declaración significa exactamente lo mismo y las llaves están implícitas. Lo omití por claridad.
JBL
2

Se llama al destructor para cada iteración. Por lo tanto, en algunos casos es más rápido declarar una variable fuera del ciclo en lugar de hacerlo en el ciclo. Suponiendo el siguiente caso:

std::string temp;
for(int i = 0; i < 10; ++i){
    temp = arr[i];
    doSomething(temp);
}

No se llama al destructor cuando se ejecuta el uso del bucle. Simplemente anula temp.

Pero si usa std::string temp = arr[i]el constructor y se llama al destructor para cada iteración. Creo que esto agrega un poco de tiempo de ejecución en caso de que tenga un bucle que se ejecuta con mucha frecuencia.

Julian Schnabel
fuente
tenga en cuenta que los destructores que se llaman o no no son solo una cuestión de rendimiento. Cuando tiene un tipo RAII, desea específicamente que se llame al destructor en cada iteración
idclev 463035818
No estoy seguro de que sea cierto. ¿No se llama al destructor del contenido 'temp' de la iteración anterior cuando se reasigna temp con el nuevo valor?
usuario1282931
Tampoco estoy 100% seguro. Corrige mi respuesta si encuentras algo mal. :)
Julian Schnabel
0

Se llama al destructor antes de la siguiente iteración

Cucharadita
fuente
0

Por supuesto, se llama a dtor al final de la iteración y el desenrollado del bucle no debería modificar este comportamiento, como cualquier otra optimización (una optimización no debería modificar el comportamiento del programa), excepto algún tipo de RVO y similares que pueden eliminar algunas creaciones de objetos semánticamente espurios .

Jean-Baptiste Yunès
fuente