https://en.cppreference.com/w/cpp/language/lifetime dans La section Notes contient ce code, reproduit ici:
struct A {
int* p;
~A() { std::cout << *p; } // if n outlives a, prints 123
};
void f() {
A a;
int n = 123; // if n does not outlive a, this is optimized out (dead store)
a.p = &n;
}
Qu'est-ce qu'il essaie de dire dans cette section Notes ?
D'après ce que je comprends, le code est UB (ou est-ce) car il est clair que n
ne survit pas a
.
Qu'est-ce que cela signifie par:
différence dans les règles de fin de vie entre les objets non-classe (fin de la durée de stockage) et les objets classe (ordre de construction inverse)
Mais cela ne dit pas que la matière comment .
Je suis très confus par toute cette section.
C'est un aspect étrange des règles de vie de C++. [basic.life]/1 nous dit que la vie d'un objet se termine:
- si
T
est un type de classe avec un destructeur non trivial ([class.dtor]), l'appel du destructeur démarre, ou- le stockage occupé par l'objet est libéré ou est réutilisé par un objet qui n'est pas imbriqué dans o ([intro.object]).
Je souligne. int
n'est pas un "type de classe avec un destructeur non trivial", donc sa durée de vie ne se termine que lorsque le stockage qu'il occupe est libéré. En revanche, A
est un type de classe avec un destructeur non trivial ", donc sa durée de vie se termine lorsque le destructeur est appelé.
Le stockage d'une étendue est libéré lorsque l'étendue se termine, conformément à [basic.stc.auto]/1 :
Le stockage des [variables avec durée de stockage automatique] dure jusqu'à ce que le bloc dans lequel elles sont créées se ferme.
Mais les variables automatiques sont détruites en accord avec [stmt.jump]/2 :
À la sortie d'une étendue (quelle que soit sa réalisation), les objets avec une durée de stockage automatique qui ont été construits dans cette étendue sont détruits dans l'ordre inverse de leur construction.
Notez que l'ordre de destruction est spécifié, mais que l'ordre de libération automatique du stockage n'est pas spécifié. Cela signifie que l'implémentation pourrait libérer le stockage juste après la destruction de chaque variable, ou le libérer tout d'un coup plus tard, ou dans un autre ordre arbitraire.
Maintenant, le fait qu'il utilise le singulier pour le stockage ("le stockage pour ... dure") plutôt que de parler de chaque variable individuellement peut suggérer que l'intention est que le stockage dans son ensemble soit libéré immédiatement pour cette étendue. Mais il n'y a pas de déclaration explicite à ce sujet dans la norme. Donc, tant qu'une variable est détruite avant que son stockage ne soit libéré, tout ordre de destruction vs libération semble être légal.
Cela signifie qu'il est tout à fait possible que le code fonctionne, que n
survive a
. Mais il n'est pas spécifié si cela fonctionne .
Cet exemple est emprunté à Core Language Issue 2256 :
Section: 6.8 [basic.life] Statut: rédaction Auteur: Richard Smith Date: 2016-03-30
Selon 6.4 [basic.lookup] puce 1.4, l'exemple suivant a défini un comportement car la durée de vie de
n
s'étend jusqu'à ce que son stockage soit libéré, ce qui est après l'exécution du destructeur dea
:void f() { struct A { int *p; ~A() { *p = 0; } } a; int n; a.p = &n; }
Il serait plus cohérent que la fin de la vie de tous les objets, qu'ils aient ou non un destructeur non trivial, soient traités de la même manière.
Notes de la réunion de mars 2018:
Le CWG est d'accord avec l'orientation suggérée.
L'idée clé est de savoir si la durée de vie d'un objet se termine à sa destruction ou au moment où sa mémoire est libérée peut affecter la sémantique d'un programme. Dans l'exemple,
si la durée de vie de n
se termine à la destruction de n
, le programme n'est pas défini;
si la durée de vie de n
se termine jusqu'à ce que la mémoire soit libérée, le programme a défini un comportement1.
Par conséquent, cela nécessite une discussion plus approfondie pour déterminer quand la durée de vie d'un objet se termine.
1 En effet Core Language Issue 2115 :
Section: 9.6 [stmt.jump] Statut: rédaction Auteur: Richard Smith Date: 2015-04-16
L'ordre relatif entre la destruction des variables automatiques à la sortie d'un bloc et la libération du stockage des variables n'est pas spécifié par la norme: tous les destructeurs sont-ils exécutés en premier puis le stockage libéré, ou sont-ils entrelacés?
Notes de la réunion de février 2016:
Le CWG a convenu que le stockage devrait persister jusqu'à ce que toutes les destructions soient terminées, bien que la règle "comme si" permettrait des optimisations non observables de cet ordre.
L'intention est que la libération en mémoire des variables automatiques se produise une fois toutes les destructions terminées.