Supprimer le destructeur de lancement appelé depuis le destructeur
Je comprends que les règles actuelles en C ++ disent que:
si un destructeur lance alors que la pile se déroule déjà à cause d'une exception, alors
std::terminate
est appelé.
En explorant pourquoi les règles sont telles qu'elles sont, je suis tombé sur la situation décrite dans le code ci-dessous.
Le destructeur des
X
lancers.Y
supprime unX
dans son propre destructeur.Par conséquent, le destructeur des
Y
lancers.
Ce n'est pas clair pour moi si le fait que les Y
lancers (3.) doivent être déclenchés std::terminate
par les règles standard. J'espère qu'il ne devrait pas, et tester contre des gcc
courses comme je l'espérais.
Une personne familière avec le jargon juridique standard peut-elle clarifier cela? Devrait (3.) déclencher std::terminate
ou non?
#include <iostream>
struct X {
~X() noexcept(false) {
std::cout << "Destroying X\n";
throw std::runtime_error("Exception");
}
};
struct Y {
X * x_;
explicit Y(X * x) : x_{x} { }
~Y() noexcept(false) {
std::cout << "Destroying Y\n";
delete x_;
}
};
int main() {
try {
Y y(new X());
std::cout << "Living\n";
}
catch (const std::exception & e) {
std::cout << "Caught " << e.what() << '\n';
}
}
Avec g ++ version 5.4.0-6ubuntu1 ~ 16.04.9 avec --std=c++17
j'obtiens:
Living
Destroying Y
Destroying X
Caught Exception
La norme dit dans [except.terminate] p1.4 :
- lorsque la destruction d'un objet pendant le déroulement de la pile se termine par la levée d'une exception, ou
2) se produit parce qu'il y
est hors de portée. 1) lève une exception, qui démarre le déroulement de la pile. Pendant le déroulement de la pile, plus précisément lors de la destruction Y
- qui est de 2). Il lève une exception, et donc le point est satisfait et std::terminate
est appelé.
C'est exactement ce que fait votre code, à l'exception d'un point important: lors du déroulement de la pile . Le déroulement de la pile ne se produit pas à la fin d'une étendue, cela se produit uniquement lorsqu'une exception est levée et sort de la portée actuelle dans laquelle elle a été levée.
Y
n'est pas détruit à cause d'une exception. Ajoutez un throw 1;
pour voir un appel à l' std::terminate
action.
Ainsi, cette clause ne s'applique pas et votre code est en effet valide.