É garantido que o destruidor de um objeto local dentro de um loop seja chamado antes da próxima iteração?

11

Quando eu tenho um loop e, dentro desse loop, crio uma nova variável de pilha (sem alocá-la no heap e com a variável declarada dentro do corpo do loop), é garantido que o destruidor desse objeto será chamado antes do início da próxima iteração ou poderá loop desenrolando pelo compilador mudar algo sobre isso?

user1282931
fonte
11
O desenrolar do loop em si não altera a ordem de execução. No entanto, a paralelização de loop pode fazer isso.
Adrian Mole

Respostas:

8

De n4800:

§6.3.3 Escopo do bloco :

Um nome declarado em um bloco (8.3) é local para esse bloco; tem escopo de bloco. Seu escopo potencial começa no ponto da declaração (6.3.2) e termina no final do seu bloco. Uma variável declarada no escopo do bloco é uma variável local.

§10.3.6 Destrutores :

Um destruidor é chamado implicitamente [...] quando o bloco no qual um objeto é criado sai (8.7)

§4.1.1 Máquina abstrata :

Essa disposição é algumas vezes chamada de regra "como se", porque uma implementação é livre para desconsiderar qualquer requisito deste documento, desde que o resultado seja como se o requisito tivesse sido obedecido, na medida em que possa ser determinado pelo comportamento observável de o programa .

[Ênfase minha]

Então sim. Sua variável fica fora de escopo no final do loop (que é um bloco) e, portanto, seu destruidor é chamado na medida em que qualquer pessoa que observe o comportamento do programa possa perceber .

Paul Evans
fonte
11
Não há nada sobre quando o destruidor é chamado.
Stark
2
@stark O que lhes permite fazer isso é a regra como se. O padrão especifica apenas o comportamento da máquina abstrata. Não tenho certeza se é necessário entrar em todos esses detalhes nas respostas aqui.
precisa
2
@stark Isso é, na OMI, irrelevante para a questão. Você também pode dizer que os destruidores podem estar embutidos e, portanto, nem serem editados call. Ou, se eles efetivamente (como regra) não fazem nada, pode não haver montagem para esses destruidores gerados.
Daniel Langr
2
@stark Consulte O que exatamente é a regra "como se"? .
Daniel Langr
2
@stark Onde é definido o quê ? Observe que esta discussão está fora do tópico da pergunta. Você pode fazer outra pergunta separada sobre esse problema.
Daniel Langr 6/12/19
8

Sim. É mais fácil visualizar quando você considera os "blocos" nos quais declara uma variável, ou seja, entre qual par de chaves. O loop é um bloco em si e, quando atinge o colchete de fechamento, antes da próxima iteração, todos os destruidores de variáveis ​​de armazenamento automático declaradas no loop são chamados.

pode desenrolar loop pelo compilador mudar alguma coisa sobre isso?

Como regra geral, não pense no que o compilador otimizará, pois ainda precisa garantir o comportamento do seu programa, não importa o que ele faça para otimizá-lo. Nesse caso, o desenrolar do loop não mudará nada nesse sentido se isso acontecer.

JBL
fonte
2
+1 para a regra geral, ao escrever o código, não se deve preocupar com os componentes internos do compilador. Iria adicionar algo do mesmo espírito à minha resposta, mas agora já está lá #
idclev 463035818/06/06/19
copy-elision e RVO mudam o comportamento do programa, não é?
Jean-Baptiste Yunès
@ Jean-BaptisteYunès Eles podem potencialmente, no entanto, o padrão também os permite por #[class.copy.elision]
ChrisMM
Não apenas par de aparelho . Você pode escrever for(...) X x{};e o xobjeto será construído + destruído em cada iteração. Demonstração ao vivo . Uma seção padrão relevante é stmt.iter / 2 .
Daniel Langr
@DanielsaysreinstateMonica De acordo com o §9.5.2 [stmt.iter], é puramente equivalente (ênfase minha): "Se a subestação em uma declaração de iteração for uma declaração única e não uma declaração composta, é como se tivesse sido reescrita para ser uma declaração composta contendo a declaração original ". Em essência, com ou sem chaves para uma única declaração significa exatamente a mesma coisa e as chaves estão implícitas. Eu o omiti por clareza.
JBL #
2

O destruidor é chamado para todas as iterações. Assim, em alguns casos, é mais rápido declarar uma variável fora do loop em vez de no loop. Supondo o seguinte caso:

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

O destruidor não é chamado quando o loop é executado. Apenas substitui temp.

Mas se você usa std::string temp = arr[i] o construtor e o destruidor, será chamado para cada iteração. Eu acho que isso adiciona um pouco de tempo de execução, caso você tenha um loop que é executado com muita frequência.

Julian Schnabel
fonte
observe que os destruidores que são chamados ou não não são apenas uma questão de desempenho. Quando você tem um tipo RAII, deseja especificamente que o destruidor seja chamado em todas as iterações
idclev 463035818
não tenho certeza se isso é verdade. O destruidor do conteúdo 'temp' da retenção da iteração anterior não é chamado exatamente quando temp é reatribuído com o novo valor?
user1282931
Também não tenho 100% de certeza. Corrija minha resposta se você encontrou algo errado. :)
Julian Schnabel
0

O destruidor é chamado antes da próxima iteração

Girspoon
fonte
0

Obviamente, o dtor é chamado no final da iteração e o desenrolamento de loop não deve modificar esse comportamento, como qualquer outra otimização (uma otimização não deve modificar o comportamento do programa), exceto algum tipo de RVO e similar que pode eliminar algumas criações de objetos semanticamente espúrios .

Jean-Baptiste Yunès
fonte