Ответ 1
Компиляторы попадают в дилемму здесь по следующим причинам:
(1) Не указывать никаких исключений в объявлении функции (т.е. не использовать throw
и noexcept
(что эквивалентно noexcept(true)
)) означает, что эта функция может выбрасывать все возможные исключения:
(§15.4/12, акцент мой) Функция с исключение-спецификацией или с исключением-спецификацией формы
noexcept(constant-expression)
, гдеconstant-expression
даетfalse
разрешает все исключения. [...]
(2) Деструктор по умолчанию по умолчанию должен позволять точно исключение, разрешенное функциями, непосредственно вызываемыми его неявным определением:
(§ 15.4/14, акцент мой) Неявно объявленная специальная функция-член (пункт 12) должна иметь спецификацию исключения. Если f - неявно объявленный конструктор по умолчанию, конструктор копирования, конструктор перемещения, деструктор, оператор присваивания копий или оператор переадресации, его неявное исключение - спецификация определяет type-id T тогда и только тогда, когда T разрешено исключением-спецификацией функции, непосредственно вызываемой fs неявным определением; f разрешает все исключения, если какая-либо функция, которую он вызывает напрямую, допускает все исключения, а f не допускает исключений, если каждая вызываемая им функция не позволяет исключений.
(3) Когда специальный член (например, деструктор) явно дефолт, т.е. когда вы используете = default
, спецификация исключения необязательная (см. использование "может иметь" ниже ):
(8.4.2/2, выделение мое) явно дефолтная функция [...] может иметь явное исключение-спецификацию только в том случае, если она совместима ( 15.4) с исключением - указание на неявное объявление. [...]
В стандарте нет инструкции, требующей спецификации исключения в явно деструкторе по умолчанию.
Заключение: Поэтому не указывать исключения явно дефолтного деструктора можно интерпретировать двумя способами:
- Чтобы означать, что все исключения разрешены (согласно (1) выше)
- Или, в качестве альтернативы, означать, что допускаются те же исключения, которые допускаются по неявному определению деструктора по умолчанию (согласно (3) выше), что в вашем случае означает исключение исключений (согласно (2) ) выше).
К сожалению, GCC разрешает эту дилемму одним способом (в пользу "исключений" ) в случае вашего объявления базового класса и по-другому в случае производного класса (в пользу "всех исключений" ) там).
Я считаю, что наиболее естественная интерпретация этой, по общему признанию, неоднозначной ситуации заключается в том, чтобы предположить, что (2) и (3) переопределить (1). Стандарт не говорит об этом, но должен. Под этой интерпретацией, Кланг, кажется, здесь.