Пустой деструктор против буквального деструктора

Рассмотрим следующий код:

#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 не буквально.

Почему и в чем разница между пустым деструктором и вообще без деструктора?

РЕДАКТИРОВАТЬ: Другой связанный вопрос: если пустые и буквальные деструкторы отличаются, как определить защищенный буквальный деструктор?

16

Решение

Цитаты из 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
20

Другие решения

Никакой деструктор не заставляет компилятор добавлять тривиальный деструктор, который плохо определен в спецификации, но в основном ничего не делает.

Если вы укажете деструктор, он не добавит тривиальный деструктор. Ваш деструктор нетривиален.

В твоем случае, 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, — это тривиальный деструктор, предоставляемый компилятором.

0

По вопросам рекламы [email protected]