以下代码在gcc 9.1 godbolt中编译,但在clang 8 godbolt中不编译:
class A {
protected:
~A() = default;
};
class B final : public A {
};
int main() {
auto b = B{};
}
C语的错误:
<source>:10:16: error: temporary of type 'A' has protected destructor
auto b = B{};
^
<source>:3:5: note: declared protected here
~A() = default;
^
哪个是正确的,为什么?
答案 0 :(得分:2)
感谢评论中的澄清;
从C ++ 17开始,B{}
是聚合的,即使它是从A
派生出来的,因此将为无法访问{的用户创建一个临时A
来进行聚合的初始化{1}}。因此clang在拒绝编译时是正确的。标准:
no virtual, private, or protected (since C++17) base classes
不过,按照标准说明使用dtor
即可。
基地的()
可以是公共的或受保护的。
一个常见的指导原则是,必须将基类的析构函数作为 公共和虚拟或受保护和非虚拟
与C ++ 11相反,在C ++中,表达式dtor
是B()
,而表达式prvalue
是移动构造,并且该移动可能会被忽略。 17,没有动静。 auto b = B();
未被移走。这是prvalue
的值初始化,并且完全等同于:
B()
答案 1 :(得分:1)
是的,Clang 拒绝代码是正确的。
在 auto b = B{};
中,我们有一个聚合初始化,它直接发生在 main
函数中。所以这个函数必须能够在初始化过程中发生异常时调用B
子类型的析构函数。
引自 N4861(最后一个 C++20 草案),[dcl.init.aggr]/8:
<块引用>可能会调用每个类类型元素的析构函数 来自聚合初始化发生的上下文。 [ 笔记: 此规定确保可以调用析构函数 完全构造的子对象,以防抛出异常。 - 结尾 注意]
为了完整起见,引用[class.dtor]/15:
<块引用>[...] 如果析构函数可能是错误的,则程序是格式错误的 调用被删除或无法从上下文访问 调用。