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

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

#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 не является литералом.

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

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


person Vincent    schedule 10.01.2013    source источник
comment
У вас не может быть нетривиального деструктора в объекте constexpr.   -  person chris    schedule 10.01.2013
comment
Защищенный деструктор = почти никогда не бывает хорошей идеей.   -  person Konrad Rudolph    schedule 10.01.2013
comment
Они нужны мне для некоторых абстрактных классов CRTP.   -  person Vincent    schedule 10.01.2013
comment
Разница: 12.4 Деструкторы [class.dtor] 3) [...] Деструктор является тривиальным, если он объявлен неявно и если: [...]   -  person Luchian Grigore    schedule 10.01.2013
comment
Вы можете сделать ~Test() = default;, я думаю.   -  person Xeo    schedule 10.01.2013
comment
@Vincent В таком случае, почему бы не сделать конструктор protected вместо деструктора?   -  person Konrad Rudolph    schedule 10.01.2013
comment
@KonradRudolph: я задал вопрос здесь: stackoverflow.com/q/14256208/882932   -  person Vincent    schedule 10.01.2013
comment
@KonradRudolph Если какой-то класс предназначен для использования в качестве базового класса для других, его деструктор должен быть либо виртуальным, либо защищенным, чтобы предотвратить UB при удалении производного объекта с помощью базового указателя.   -  person Tadeusz Kopec    schedule 10.01.2013
comment
@TadeuszKopec Просто вам не нужно возиться с виртуальными деструкторами, когда вы вообще не собираетесь полиморфно уничтожать объекты.   -  person Christian Rau    schedule 10.01.2013
comment
@Tadeusz Если класс не полиморфен, то как, черт возьми, вы получили указатель/ссылку на него через его базу? Это ошибка, вы не должны использовать CRTP таким образом.   -  person Konrad Rudolph    schedule 10.01.2013
comment
@Tadeusz Я думаю, что нет смысла защищаться от того, что не произойдет случайно.   -  person R. Martinho Fernandes    schedule 10.01.2013


Ответы (2)


Цитаты из n3376

7.1.5/9

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression

3.9/10

A type is a literal type if:

у него тривиальный деструктор...

12.4/5

A destructor is trivial if it is not user-provided and if:

— деструктор не виртуальный,

- все прямые базовые классы своего класса имеют тривиальные деструкторы, и

- для всех нестатических членов данных его класса, которые имеют тип класса (или их массив), каждый такой класс имеет тривиальный деструктор.

В противном случае деструктор нетривиален.

Диагностика clang действительно более информативна:

error: constexpr variable cannot have non-literal type 'const C'

'C' is not literal because it has a user-provided destructor
person ForEveR    schedule 10.01.2013
comment
Clang: 1254, Все остальные: 32 - person chris; 10.01.2013
comment
@chris: Извините за мое незнание, но что означает ваш комментарий? - person user1284631; 10.01.2013
comment
@axeoth, это была игра о том, насколько хорош Clang, его ошибки читаемы и / или полезны по сравнению с другими компиляторами. - person chris; 10.01.2013

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

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

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

person Cort Ammon    schedule 06.09.2013