Ars Longa, Vita Brevis

Откуда берется looser throw specifier

Имеется кусок кода:

020c1899f647dc199cfb62ceecfc9113a15f5d630000

При компиляции в GCC выдает интересную ошибку:

020c1899f647dc199cfb62ceecfc9113a15f5d630001

Самое интересное, что если сделать Derived::s типа, например, int, то ошибка исчезнет.

В чем же дело?

Во втором случае компилятор генерирует автоматический деструктор с сигнатурой virtual Derived::~Derived() throw(), в первом — просто virtual Derived::~Derived(). Почему же так происходит? Как оказалось, всё просто (отвлекусь: вспомнил старый универский анекдот: стоит профессор у доски и говорит: "Как бы это доказать? Или это очевидно?" Через три часа возвращается уставший студент с толстой распечаткой и говорит: "Действительно очевидно."). На самом деле, при генерации деструктора по умолчанию компилятор рассматривает сигнатуры деструкторов всех статических (не в плане static, а в плане "не динамических") членов класса, и выбирает такую сигнатуру, которая является пересечением всех рассмотренных сигнатур. Дело в том, что деструктор ::std::basic_string не имеет спецификации throw(), и поэтому наиболее совместимым является генерация автоматического деструктора без спецификации throw(). Расчет здесь прост: если компилятор сгенерирует деструктор со спецификацией throw(), а деструктор ::std::basic_string бросит исключение, то это будет нарушением спецификации и, следовательно, будет очень плохо (вообще, на мой взгляд, кидаться исключениями в деструкторе — дурной тон).

Поэтому правильные деструкторы приходится в таких случаях прописывать руками…