Ответ
Начиная с C++11, компилятор автоматически генерирует деструктор с спецификацией noexcept, если он не объявлен пользователем как noexcept(false) или если все деструкторы членов класса и базовых классов также noexcept. Это сделано для обеспечения гарантии безопасности исключений во время одной из самых критичных операций — уничтожения объектов.
Основная причина: Предотвратить катастрофическое завершение программы. Если во время раскрутки стека (stack unwinding), вызванной другим исключением, деструктор выбрасывает новое исключение, программа немедленно вызывает std::terminate(). Неявное noexcept делает такое поведение предсказуемым и предотвращает неопределённое состояние.
#include <iostream>
struct Resource {
~Resource() { std::cout << "Resource cleanedn"; }
};
struct Safe {
// Компилятор сгенерирует: ~Safe() noexcept = default;
Resource res;
};
struct Dangerous {
~Dangerous() noexcept(false) { // Явно разрешаем исключения
throw std::runtime_error("Exception in dtor!");
}
};
int main() {
try {
Safe s; // Деструктор noexcept
throw 42; // Начало раскрутки стека
// Деструктор 's' будет вызван, исключений не бросит.
} catch (...) {}
try {
Dangerous d;
throw 42; // Раскрутка стека начнется
// Вызов ~Dangerous() бросит исключение ВО ВРЕМЯ раскрутки -> std::terminate()!
} catch (...) {
// Сюда управление не дойдет
}
return 0;
}
Практическое правило: Деструкторы должны быть noexcept и никогда не должны выбрасывать исключения. Если деструктор выполняет операции, которые могут завершиться ошибкой (например, закрытие файла или сетевого соединения), эти ошибки должны быть обработаны внутри деструктора (залогированы, проигнорированы), но не покидать его.