Le destructeur d'un objet local dans une boucle est-il garanti d'être appelé avant la prochaine itération?

11

Lorsque j'ai une boucle et à l'intérieur de cette boucle, crée une nouvelle variable de pile (ne pas l'allouer sur le tas et la variable la tenant déclarée à l'intérieur du corps de la boucle), le destructeur de cet objet est-il garanti d'être appelé avant le début de la prochaine itération, ou pourrait boucle déroulant par le compilateur changer quelque chose à ce sujet?

user1282931
la source
1
Le déroulement de la boucle en soi ne changera pas l'ordre d'exécution. Cependant, la parallélisation de boucle pourrait le faire.
Adrian Mole

Réponses:

8

De n4800:

§6.3.3 Portée du bloc :

Un nom déclaré dans un bloc (8.3) est local à ce bloc; il a une portée de bloc. Sa portée potentielle commence à son point de déclaration (6.3.2) et se termine à la fin de son bloc. Une variable déclarée à la portée du bloc est une variable locale.

§10.3.6 Destructeurs :

Un destructeur est invoqué implicitement [...] lorsque le bloc dans lequel un objet est créé se termine (8.7)

§4.1.1 Machine abstraite :

Cette disposition est parfois appelée la règle du «comme si», car une mise en œuvre est libre d'ignorer toute exigence de ce document tant que le résultat est comme si l'exigence avait été respectée, dans la mesure où cela peut être déterminé à partir du comportement observable de le programme .

[Je souligne]

Donc oui. Votre variable sort du domaine à la fin de la boucle (qui est un bloc) et donc son destructeur est appelé pour autant que quiconque observe le comportement du programme puisse le dire .

Paul Evans
la source
1
Rien là-dedans sur QUAND le destructeur est appelé.
Stark
2
@stark Ce qui leur permet de le faire, c'est la règle du «comme si». La norme spécifie uniquement le comportement de la machine abstraite. Je ne sais pas s'il est nécessaire d'entrer dans tous ces détails dans les réponses ici.
Max Langhof
2
@stark Ceci est, l'OMI, sans rapport avec la question. Autant dire que les destructeurs peuvent être alignés, et donc pas calldu tout édités . Ou, s'ils ne font rien (en règle générale), aucun assemblage pour ces destructeurs ne peut être généré.
Daniel Langr
2
@stark Voir Qu'est-ce que la règle «comme si»? .
Daniel Langr
2
@stark Où est défini quoi ? Notez que cette discussion est hors sujet par rapport à la question. Vous pouvez poser une autre question distincte sur ce problème.
Daniel Langr
8

Oui. Il est plus facile de visualiser lorsque vous considérez les "blocs" dans lesquels vous déclarez une variable, c'est-à-dire entre quelle paire d'accolades. La boucle est un bloc en soi, et lorsqu'elle atteint la parenthèse fermante, avant la prochaine itération, tous les destructeurs de variables de stockage automatique déclarés dans la boucle sont appelés.

le déroulement du bouclage par le compilateur pourrait-il changer quelque chose?

En règle générale, ne pensez pas à ce que le compilateur optimisera, car il doit encore garantir le comportement de votre programme, quoi qu'il fasse pour l'optimiser. Dans ce cas, le déroulement de la boucle ne changera rien à cet effet s'il se produit.

JBL
la source
2
+1 pour la règle générale, lors de l'écriture de code, il ne faut pas se soucier des internes du compilateur. J'allais ajouter quelque chose dans le même esprit à ma réponse, mais maintenant c'est déjà là
idclev 463035818
copy-elision et RVO changent le comportement du programme, n'est-ce pas?
Jean-Baptiste Yunès
@ Jean-BaptisteYunès Ils peuvent potentiellement, cependant, la norme les autorise aussi par[class.copy.elision]
ChrisMM
Pas seulement des bretelles . Vous pouvez écrire for(...) X x{};et l' xobjet sera construit + détruit à chaque itération. Démo en direct . Une section standard pertinente est stmt.iter / 2 .
Daniel Langr
@DanielsaysreinstateMonica Selon le §9.5.2, [stmt.iter]il est purement équivalent (c'est moi qui souligne): "Si la sous-déclaration d'une instruction d'itération est une instruction unique et non une instruction composée, c'est comme si elle avait été réécrite pour être une instruction composée contenant la déclaration d'origine. ". En substance, avec ou sans les accolades pour une seule instruction signifie exactement la même chose et les accolades sont implicites. Je l'ai omis pour plus de clarté.
JBL
2

Le destructeur est appelé pour chaque itération. Ainsi, dans certains cas, il est plus rapide de déclarer une variable en dehors de la boucle plutôt que dans la boucle. En supposant le cas suivant:

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

Le destructeur n'est pas appelé lorsque la boucle est exécutée. Il remplace simplement temp.

Mais si vous utilisez std::string temp = arr[i]le constructeur et le destructeur est appelé pour chaque itération. Je pense que cela ajoute un peu d'exécution au cas où vous auriez une boucle qui est exécutée très souvent.

Julian Schnabel
la source
notez que les destructeurs appelés ou non ne sont pas seulement une question de performances. Lorsque vous avez un type RAII, vous souhaitez spécifiquement que le destructeur soit appelé à chaque itération
idclev 463035818
pas sûr que ce soit vrai. Le destructeur du contenu «temp» contenu dans l'itération précédente n'est-il pas appelé juste lorsque temp est réaffecté avec la nouvelle valeur?
user1282931
Je ne suis pas sûr à 100% non plus. Corrigez ma réponse si vous avez trouvé quelque chose de mal. :)
Julian Schnabel
0

Le destructeur est appelé avant la prochaine itération

Girspoon
la source
0

Bien sûr, dtor est appelé à la fin de l'itération et le déroulement de la boucle ne devrait pas modifier ce comportement, comme toute autre optimisation (une optimisation ne devrait pas modifier le comportement du programme), à ​​l'exception d'une sorte de RVO et similaire qui peut éliminer certaines créations d'objets sémantiquement parasites .

Jean-Baptiste Yunès
la source