Приводит ли этот код к материализации базового значения и должен ли он компилироваться?
Следующий код компилируется в 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();