9

以下代码在 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;
    ^

哪个是正确的,为什么?

4

2 回答 2

2

感谢评论中的澄清;由于 C++17B{}是聚合的,即使它是从 派生的A,因此A无权访问dtor. 所以clang拒绝编译是正确的。标准:

没有虚拟、私有或受保护 (C++17 起) 基类

然而,使用()将按照标准所说的那样工作。

基础的dtor可以是公共的或受保护的。

一个共同的准则是基类的析构函数必须是公共的和虚拟的或受保护的和非虚拟的

见标准指南

与 C++11 相比,表达式B()是 a prvalue,并且auto b = B();是一个移动构造,移动可能会被忽略,在 C++17 中,没有移动。prvalue不搬离。这是值初始化B(),完全等同于:

B();

C++17 中的值类别

此代码是否应该无法在 C++17 中编译?

于 2019-06-07T16:59:44.737 回答
1

是的,Clang 拒绝代码是正确的。

auto b = B{};我们有一个聚合初始化,它直接发生在main函数中。所以这个函数必须能够B在初始化过程中发生异常时调用子类型的析构函数。

引自 N4861(最后的 C++20 草案),[dcl.init.aggr]/8

类类型的每个元素的析构函数可能从发生聚合初始化的上下文中调用。[注意:此规定确保可以为完全构造的子对象调用析构函数,以防抛出异常。——尾注]

为了完整起见,引用[class.dtor]/15

[...]如果可能调用的析构函数被删除或无法从调用的上下文中访问,则程序是不正确的。

于 2021-08-07T11:35:16.073 回答