Meine Frage lautet also: Wenn das Werfen von einem Destruktor zu undefiniertem Verhalten führt, wie gehen Sie mit Fehlern um, die während eines Destruktors auftreten?
Das Hauptproblem ist folgendes: Sie können nicht scheitern, um zu scheitern . Was bedeutet es schließlich, nicht zu scheitern? Was passiert mit der Integrität unserer Daten, wenn das Festschreiben einer Transaktion in eine Datenbank fehlschlägt und nicht fehlschlägt (Rollback fehlschlägt)?
Da Destruktoren sowohl für normale als auch für außergewöhnliche (fehlgeschlagene) Pfade aufgerufen werden, können sie selbst nicht fehlschlagen, oder wir "versagen nicht".
Dies ist ein konzeptionell schwieriges Problem, aber häufig besteht die Lösung darin, nur einen Weg zu finden, um sicherzustellen, dass ein Fehlschlag nicht fehlschlagen kann. Beispielsweise kann eine Datenbank Änderungen schreiben, bevor sie in eine externe Datenstruktur oder Datei übernommen wird. Wenn die Transaktion fehlschlägt, kann die Datei- / Datenstruktur weggeworfen werden. Alles, was es dann sicherstellen muss, ist, dass das Festschreiben der Änderungen von dieser externen Struktur / Datei eine atomare Transaktion ist, die nicht fehlschlagen kann.
Die pragmatische Lösung besteht vielleicht darin, sicherzustellen, dass die Wahrscheinlichkeit eines Scheiterns astronomisch unwahrscheinlich ist, da es in einigen Fällen fast unmöglich sein kann, Dinge unmöglich zu machen, um zu scheitern.
Die für mich am besten geeignete Lösung besteht darin, Ihre Nicht-Bereinigungslogik so zu schreiben, dass die Bereinigungslogik nicht fehlschlagen kann. Wenn Sie beispielsweise versucht sind, eine neue Datenstruktur zu erstellen, um eine vorhandene Datenstruktur zu bereinigen, sollten Sie diese Hilfsstruktur möglicherweise im Voraus erstellen, damit wir sie nicht mehr in einem Destruktor erstellen müssen.
Das ist zwar viel leichter gesagt als getan, aber es ist der einzig richtige Weg, den ich sehe. Manchmal denke ich, dass es die Möglichkeit geben sollte, eine separate Destruktorlogik für normale Ausführungspfade zu schreiben, die von außergewöhnlichen entfernt ist, da Destruktoren manchmal das Gefühl haben, dass sie die doppelte Verantwortung haben, wenn sie versuchen, mit beiden umzugehen (ein Beispiel sind Scope Guards, die eine explizite Entlassung erfordern ; sie würden dies nicht benötigen, wenn sie außergewöhnliche Zerstörungspfade von nicht außergewöhnlichen unterscheiden könnten).
Das ultimative Problem ist jedoch, dass wir nicht versagen können, und es ist ein schwieriges Konzeptentwurfsproblem, das in allen Fällen perfekt zu lösen ist. Es wird einfacher, wenn Sie sich nicht zu sehr in komplexe Kontrollstrukturen mit Tonnen von kleinen Objekten einwickeln, die miteinander interagieren, und stattdessen Ihre Entwürfe etwas voluminöser modellieren (Beispiel: Partikelsystem mit einem Destruktor, um das gesamte Partikel zu zerstören System, kein separater nicht trivialer Destruktor pro Partikel). Wenn Sie Ihre Entwürfe auf dieser gröberen Ebene modellieren, müssen Sie sich mit weniger nicht trivialen Destruktoren befassen und können sich häufig den erforderlichen Speicher- / Verarbeitungsaufwand leisten, um sicherzustellen, dass Ihre Destruktoren nicht ausfallen können.
Und das ist natürlich eine der einfachsten Lösungen, Destruktoren seltener zu verwenden. Im obigen Partikelbeispiel sollten möglicherweise beim Zerstören / Entfernen eines Partikels einige Dinge getan werden, die aus irgendeinem Grund fehlschlagen können. In diesem Fall wird stattdessen eine solche Logik durch die Partikel des dtor von Aufrufen , die in einem außergewöhnlichen Weg durchgeführt werden könnte, könnte man stattdessen haben sie alle durch die Partikel - System durchgeführt , wenn es beseitigt ein Teilchen. Das Entfernen eines Partikels kann immer auf einem nicht außergewöhnlichen Weg erfolgen. Wenn das System zerstört wird, kann es möglicherweise nur alle Partikel entfernen und sich nicht mit der einzelnen Partikelentfernungslogik befassen, die fehlschlagen kann, während die Logik, die fehlschlagen kann, nur während der normalen Ausführung des Partikelsystems ausgeführt wird, wenn ein oder mehrere Partikel entfernt werden.
Es gibt oft solche Lösungen, die auftauchen, wenn Sie vermeiden, mit vielen kleinen Objekten mit nicht trivialen Destruktoren umzugehen. Wo Sie sich in einem Chaos verfangen können, in dem es fast unmöglich erscheint, Ausnahmesicherheit zu haben, ist, wenn Sie sich in vielen kleinen Objekten verheddern, die alle nicht triviale Dtoren haben.
Es wäre sehr hilfreich, wenn nothrow / noexcept tatsächlich in einen Compilerfehler übersetzt würde, wenn irgendetwas, das es angibt (einschließlich virtueller Funktionen, die die noexcept-Spezifikation seiner Basisklasse erben sollten), versuchen würde, alles aufzurufen, was auslösen könnte. Auf diese Weise könnten wir all diese Dinge zur Kompilierungszeit abfangen, wenn wir tatsächlich versehentlich einen Destruktor schreiben, der werfen könnte.