Приводит ли этот код к материализации базового значения и должен ли он компилироваться?

Следующий код компилируется в gcc 9.1 Godbolt, но не в Clang 8 Godbolt:

class A {
protected:
    ~A() = default;
};

class B final : public A {
};

int main() {
    auto b = B{};
}

Ошибка Clang:

<source>:10:16: error: temporary of type 'A' has protected destructor
    auto b = B{};
               ^
<source>:3:5: note: declared protected here
    ~A() = default;
    ^

Что правильно и почему?

2 ответа

Решение

Да, Clang правильно отклоняет код.

В auto b = B{}; у нас есть агрегированная инициализация, которая происходит непосредственно в mainфункция. Таким образом, эта функция должна иметь возможность вызывать деструкторы B подтипы в случае возникновения исключения при инициализации.

Цитата из N4861 (последний черновик C++20), [dcl.init.aggr] / 8:

Деструктор для каждого элемента типа класса потенциально вызывается из контекста, в котором происходит агрегированная инициализация. [Примечание: это положение гарантирует, что деструкторы могут быть вызваны для полностью сконструированных подобъектов в случае возникновения исключения. - конец примечания]

Для полноты, цитата из [class.dtor] / 15:

[...] Программа неправильно сформирована, если деструктор, который потенциально может быть вызван, удален или недоступен из контекста вызова.

Спасибо за разъяснения в комментариях; Начиная с C++17, B{} является совокупным, даже если он получен из Aитак временный A будет создан для агрегатного инициатора пользователем, который не имеет доступа к dtor, Таким образом, Clang правильно отклонить компиляцию. Стандарт:

нет виртуальных, частных или защищенных (начиная с C++17) базовых классов

Однако используя () будет работать, как говорит стандарт.

dtor базы могут быть публичными или защищенными.

Общим правилом является то, что деструктор для базового класса должен быть публичным и виртуальным или защищенным и не виртуальным

см. руководство стандарта

В отличие от C++11, где выражение B() это prvalue, а также auto b = B(); это конструкция-ход, и движение, скорее всего, будет отменено. В C++ 17 нет движения. prvalue не перемещен из. Это инициализация значения B() и в точности эквивалентно:

B();

Категории значений в C++ 17

Должен ли этот код не скомпилироваться в C++17?

Другие вопросы по тегам