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

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

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

#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: Другой связанный вопрос: если пустые и литературные деструкторы различны, как определить защищенный деструктор литерала?

4b9b3361

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