Ma question est donc la suivante: si le lancement d'un destructeur entraîne un comportement indéfini, comment gérez-vous les erreurs qui se produisent pendant un destructeur?
Le principal problème est le suivant: vous ne pouvez pas échouer . Que signifie l'échec de l'échec, après tout? Si la validation d'une transaction dans une base de données échoue et échoue (échoue à la restauration), qu'arrive-t-il à l'intégrité de nos données?
Étant donné que les destructeurs sont invoqués pour des chemins normaux et exceptionnels (échec), ils ne peuvent pas eux-mêmes échouer, sinon nous «échouons».
C'est un problème conceptuellement difficile, mais la solution consiste souvent à trouver un moyen de s'assurer que l'échec ne peut pas échouer. Par exemple, une base de données peut écrire des modifications avant de valider une structure de données ou un fichier externe. Si la transaction échoue, la structure de fichiers / données peut être jetée. Il suffit ensuite de s'assurer que la validation des modifications à partir de cette structure / fichier externe constitue une transaction atomique qui ne peut pas échouer.
La solution pragmatique consiste peut-être simplement à s'assurer que les chances d'échec en cas d'échec sont astronomiquement improbables, car rendre les choses impossibles à échouer peut être presque impossible dans certains cas.
La solution la plus appropriée pour moi est d'écrire votre logique de non-nettoyage d'une manière telle que la logique de nettoyage ne puisse pas échouer. Par exemple, si vous êtes tenté de créer une nouvelle structure de données afin de nettoyer une structure de données existante, alors vous pourriez peut-être chercher à créer cette structure auxiliaire à l'avance afin que nous n'ayons plus à la créer à l'intérieur d'un destructeur.
Certes, c'est beaucoup plus facile à dire qu'à faire, mais c'est la seule façon vraiment appropriée de procéder. Parfois, je pense qu'il devrait être possible d'écrire une logique de destructeur distincte pour les chemins d'exécution normaux loin des chemins exceptionnels, car parfois les destructeurs se sentent un peu comme s'ils ont le double des responsabilités en essayant de gérer les deux (un exemple est les gardes de portée qui nécessitent un rejet explicite ; ils ne l'exigeraient pas s'ils pouvaient différencier les voies de destruction exceptionnelles des voies non exceptionnelles).
Le problème ultime reste que nous ne pouvons pas échouer, et c'est un problème de conception difficile à résoudre parfaitement dans tous les cas. Cela devient plus facile si vous n'êtes pas trop enveloppé dans des structures de contrôle complexes avec des tonnes d'objets minuscules interagissant les uns avec les autres, et modélisez plutôt vos conceptions de manière légèrement plus volumineuse (exemple: système de particules avec un destructeur pour détruire la particule entière système, pas un destructeur non trivial séparé par particule). Lorsque vous modélisez vos conceptions à ce type de niveau plus grossier, vous avez moins de destructeurs non triviaux à gérer, et vous pouvez souvent vous permettre la surcharge de mémoire / traitement requise pour vous assurer que vos destructeurs ne peuvent pas échouer.
Et c'est naturellement l'une des solutions les plus simples qui consiste à utiliser moins souvent des destructeurs. Dans l'exemple de particules ci-dessus, peut-être lors de la destruction / suppression d'une particule, certaines choses devraient être faites qui pourraient échouer pour une raison quelconque. Dans ce cas, au lieu d'invoquer une telle logique par le biais du dtor de la particule qui pourrait être exécuté dans un chemin exceptionnel, vous pourriez plutôt tout faire par le système de particules lorsqu'il supprime une particule. La suppression d'une particule peut toujours être effectuée lors d'un trajet non exceptionnel. Si le système est détruit, il peut peut-être simplement purger toutes les particules et ne pas s'embêter avec cette logique d'élimination de particules individuelle qui peut échouer, tandis que la logique qui peut échouer n'est exécutée que pendant l'exécution normale du système de particules lorsqu'il supprime une ou plusieurs particules.
Il existe souvent des solutions comme celle-ci qui surgissent si vous évitez de traiter de nombreux objets minuscules avec des destructeurs non triviaux. Où vous pouvez vous emmêler dans un gâchis où il semble presque impossible de faire exception à la sécurité, c'est quand vous vous enchevêtrez dans de nombreux objets minuscules qui ont tous des détecteurs non triviaux.
Cela aiderait beaucoup si nothrow / noexcept se traduisait réellement en une erreur de compilation si quelque chose qui le spécifiait (y compris les fonctions virtuelles qui devraient hériter de la spécification noexcept de sa classe de base) tentait d'invoquer tout ce qui pouvait lancer. De cette façon, nous serions en mesure d'attraper tout cela au moment de la compilation si nous écrivons un destructeur qui pourrait se lancer par inadvertance.