Рассмотрим следующий код:
#include <iostream>
class Test
{
public:
constexpr Test(const int x) : _x(x) {}
constexpr int get() const {return _x;}
~Test() {} // HERE
protected:
const int _x;
};
int main()
{
static constexpr Test test(5);
return 0;
}
Если я уберу строку HERE
код компилируется хорошо, но если я определю пустой деструктор, это приведет к ошибке компиляции, говорящей, что Test
не буквально.
Почему и в чем разница между пустым деструктором и вообще без деструктора?
РЕДАКТИРОВАТЬ: Другой связанный вопрос: если пустые и буквальные деструкторы отличаются, как определить защищенный буквальный деструктор?
Цитаты из n3376
7.1.5 / 9
Спецификатор constexpr, используемый в объявлении объекта, объявляет объект как const. Такой объект должен иметь
литеральный тип и должен быть инициализирован. Если он инициализирован вызовом конструктора, этот вызов должен быть константой
выражение
3.9 / 10
Тип является литеральным типом, если:у него есть тривиальный деструктор …
12.4 / 5
Деструктор тривиален, если он не предоставлен пользователем и если:— деструктор не виртуален,
— все прямые базовые классы этого класса имеют тривиальные деструкторы, и
— для всех не статических членов данных своего класса, которые имеют тип класса (или его массив), каждый такой
класс имеет тривиальный деструктор.В противном случае деструктор нетривиален.
Clang Diagnostic действительно более информативен:
error: constexpr variable cannot have non-literal type 'const C'
'C' is not literal because it has a user-provided destructor
Никакой деструктор не заставляет компилятор добавлять тривиальный деструктор, который плохо определен в спецификации, но в основном ничего не делает.
Если вы укажете деструктор, он не добавит тривиальный деструктор. Ваш деструктор нетривиален.
В твоем случае, Test::~Test() { }
выглядит чертовски банально, но это человеческая интерпретация того, что вы видите. Чтобы идти дальше, как насчет:
Test::~Test()
{
int a = 5;
}
Мы видим, что оптимизатор может оптимизировать a, поэтому он, очевидно, ничего не делает. Как насчет:
Test::~Test()
{
for (int i = 0; i < 1000; i += 2) {
if ((i % 2) == 1)
doSomeSideEffect(); // like throwing or setting a global
}
}
Мы это видим i
никогда не может быть странным, так что деструктор ничего не делает.
Спецификация должна определить, что может быть constexpr, а что нет. Вместо того, чтобы идти по этой кроличьей норе определения «ничего не делать», они просто заявляют, что единственный деструктор, который ничего не делает, который достаточно хорош для constexpr, — это тривиальный деструктор, предоставляемый компилятором.