Итак, мой вопрос заключается в следующем: если выброс из деструктора приводит к неопределенному поведению, как вы обрабатываете ошибки, возникающие во время деструктора?
Основная проблема заключается в следующем: вы не можете потерпеть неудачу . Что значит потерпеть неудачу, в конце концов? Если фиксация транзакции в базе данных не удалась и не удалась (не удалось выполнить откат), что происходит с целостностью наших данных?
Поскольку деструкторы вызываются как для нормальных, так и для исключительных (сбойных) путей, они сами не могут потерпеть неудачу, иначе мы «не сможем потерпеть неудачу».
Это концептуально сложная проблема, но часто решение состоит в том, чтобы просто найти способ убедиться, что неудача не может закончиться неудачей. Например, база данных может записать изменения до фиксации во внешней структуре данных или файле. Если транзакция не удалась, то структура файла / данных может быть отброшена. Все, что от него требуется, - убедиться, что фиксация изменений из этой внешней структуры / файла является атомарной транзакцией, которая не может завершиться неудачей.
Прагматичное решение, возможно, состоит в том, чтобы просто убедиться, что шансы на неудачу при неудаче астрономически маловероятны, так как в некоторых случаях практически невозможно сделать так, чтобы вещи потерпели неудачу.
Самое правильное решение для меня - написать логику неочищения таким образом, чтобы логика очистки не могла потерпеть неудачу. Например, если у вас возникает желание создать новую структуру данных, чтобы очистить существующую структуру данных, возможно, вы можете заранее попытаться создать эту вспомогательную структуру, чтобы нам больше не приходилось создавать ее внутри деструктора.
Это все гораздо легче сказать, чем сделать, по общему признанию, но это единственный действительно правильный способ, которым я вижу, чтобы сделать это. Иногда я думаю, что должна быть возможность написать отдельную логику деструктора для нормальных путей выполнения, отличных от исключительных, поскольку иногда деструкторы чувствуют, что у них двойная ответственность, пытаясь справиться с обоими (например, защитники области действия, которые требуют явного увольнения они не потребовали бы этого, если бы могли отличить исключительные пути разрушения от неисключительных).
Тем не менее, конечная проблема заключается в том, что мы не можем не потерпеть неудачу, и это сложная концептуальная проблема дизайна, которую необходимо решить во всех случаях. Это становится легче, если вы не слишком запутаетесь в сложных управляющих структурах с тоннами маленьких объектов, взаимодействующих друг с другом, и вместо этого смоделируете свои проекты немного громоздким способом (например: система частиц с деструктором, чтобы уничтожить всю частицу система, а не отдельный нетривиальный деструктор на частицу). Когда вы моделируете свои проекты на таком более грубом уровне, у вас есть меньше нетривиальных деструкторов, с которыми вы можете иметь дело, и вы также можете часто позволить себе любую дополнительную нагрузку на память / обработку, чтобы ваши деструкторы не могли выйти из строя.
И это одно из самых простых решений, естественно - реже использовать деструкторы. В приведенном выше примере с частицами, возможно, при уничтожении / удалении частицы, должны быть сделаны некоторые вещи, которые могут потерпеть неудачу по любой причине. В этом случае, вместо того, чтобы вызывать такую логику через dtor частицы, который мог бы выполняться по исключительному пути, вы могли бы вместо этого сделать все это системой частиц, когда она удаляет частицу. Удаление частицы всегда может быть сделано во время неисключительного пути. Если система разрушена, возможно, она может просто очистить все частицы и не беспокоиться об этой логике удаления отдельных частиц, которая может дать сбой, в то время как логика, которая может дать сбой, выполняется только во время нормального выполнения системы частиц, когда она удаляет одну или несколько частиц.
Часто есть решения, подобные тем, которые возникают, если вы избегаете иметь дело с множеством маленьких объектов с нетривиальными деструкторами. Где вы можете запутаться в беспорядке, где кажется почти невозможным быть безопасным от исключений, - это когда вы запутываетесь в множестве маленьких объектов, у которых все есть нетривиальные dtors.
Было бы очень полезно, если бы nothrow / noexcept фактически транслировался в ошибку компилятора, если бы что-либо, что его указывает (включая виртуальные функции, которые должны наследовать спецификацию noexcept своего базового класса), попыталось вызвать что-нибудь, что может выдать. Таким образом, мы сможем поймать все эти вещи во время компиляции, если мы на самом деле напишем деструктор, который может сработать.