Празен деструктор срещу литерал деструктор

Разгледайте следния код:

#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