Пустой деструктор против буквального деструктора
Рассмотрим следующий код:
#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
не является литералом.
Почему и в чем разница между пустым деструктором и вообще отсутствием деструктора?
EDIT: Другой связанный вопрос: если пустые и литературные деструкторы различны, как определить защищенный деструктор литерала?
Ответы
Ответ 1
Цитаты из n3376
7.1.5/9
Спецификатор constexpr, используемый в объявлении объекта, объявляет объект как const. Такой объект должен иметь
буквальный тип и должен быть инициализирован. Если он инициализируется вызовом конструктора, этот вызов должен быть константой
Выражение
3,9/10
Тип - это буквальный тип, если:
имеет тривиальный деструктор...
12.4/5
Деструктор тривиален, если он не предоставляется пользователем, и если:
- деструктор не является виртуальным,
- все прямые базовые классы его класса имеют тривиальные деструкторы, а
- для всех нестатических членов данных своего класса, которые относятся к типу класса (или его массиву), каждый из таких
класс имеет тривиальный деструктор.
В противном случае деструктор является нетривиальным.
Диагностика clang действительно более информативна:
error: constexpr variable cannot have non-literal type 'const C'
'C' is not literal because it has a user-provided destructor
Ответ 2
Ни один деструктор не заставит компилятор добавить тривиальный деструктор, который плохо определен в спецификации, но в основном ничего не делает.
Если вы укажете деструктор, он не добавит тривиальный деструктор. Ваш деструктор нетривиален.
В вашем случае 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, а что нет. Вместо того, чтобы спускаться по этой кроличьей дыре, определяющей "ничего не делая", они просто заявляют, что единственный деструктор do-nothing, который достаточно хорош для constexpr, - это компилятор, предоставляемый тривиальным деструктором.