C2694 на деструкторе, когда деструктор элемента базового класса имеет непустой спецификатор noexcept и тело
Я испытываю ошибку компилятора, которую я не могу объяснить и не смог найти информацию в Интернете об этом. Недавно я добавил спецификатор noexcept
для деструктора класса-оболочки, и теперь большое количество классов, которые наследуются от классов, использующих эту оболочку, не скомпилируются. Я пробовал его с GCC 4.9 без ошибок компилятора.
Я использую Visual Studio Professional 2015 версии 14.0.25431.01 Update 3
Рассмотрим следующий минимальный код, который воспроизводит проблему:
#include <type_traits>
template<class T>
struct member
{
~member() noexcept(std::is_nothrow_destructible<T>::value) {};
};
struct parent
{
virtual ~parent() noexcept = default;
};
struct derived : public parent
{
member<int> x;
};
В этом фрагменте отображается следующее сообщение об ошибке:
1>c:\users\fandrieux\workspace\tmp\dtor_noexcept\main.cpp(19): error C2694: 'derived::~derived(void) noexcept(<expr>)': overriding virtual function has less restrictive exception specification than base class virtual member function 'parent::~parent(void) noexcept'
1> c:\users\fandrieux\workspace\tmp\dtor_noexcept\main.cpp(19): note: compiler has generated 'derived::~derived' here
1> c:\users\fandrieux\workspace\tmp\dtor_noexcept\main.cpp(12): note: see declaration of 'parent::~parent'
Интересным является то, что ошибка компилятора исчезает, если вы замените тело деструктора элемента на = default
или используйте либо noexcept
, либо noexcept(true)
:
// None of these produce compiler errors
virtual ~member() noexcept(std::is_nothrow_destructible<T>::value) = default;
virtual ~member() noexcept(true) {}
virtual ~member() noexcept {}
Я знаю, что деструктор не бросает. Параноиды и скептики (например, я) могут добавить следующее статическое утверждение и проверить себя:
static_assert(std::is_nothrow_destructible<T>::value, "Might throw!");
В соответствии с MSDN он сигнализирует о недостаточном динамическом спецификаторе исключений. Как это применимо здесь? Не является ли noexcept ([boolean expression]) эквивалентным либо noexcept(true)
, либо noexcept(false)
? Почему это изменяется в зависимости от наличия тела функции? Добавление явного noexcept деструктора в производную получает езду ошибки компилятора, но это кажется ненужным обходным путем. На практике это также довольно тяжело, если учесть, что каждый производный класс должен быть обновлен.
Ответы
Ответ 1
Это похоже на ошибку компилятора. Если мы добавим следующий класс:
struct dtor_throws
{
~dtor_throws() noexcept(false) {}
};
И измените определение derived
следующим образом:
struct derived : public parent
{
member<dtor_throws> x;
};
Затем GCC и Clang жалуются, что спецификация исключения ~derived
меньше, чем ~parent
.
В исходном примере MSVC, похоже, не встраивает значение выражения noexcept
в тип ~parent
, а просто обрабатывает спецификации всех составных noexcept
определяемых пользователем деструкторов шаблонов классов как более менее noexcept(true)
.
MSVC 2017 RC также затронут.