此代码是否会产生物化的基本prvalue,并应进行编译?

时间:2019-06-07 16:00:44

标签: c++ language-lawyer c++17 destructor compiler-generated

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

哪个是正确的,为什么?

2 个答案:

答案 0 :(得分:2)

感谢评论中的澄清; 从C ++ 17开始,B{}是聚合的,即使它是从A派生出来的,因此将为无法访问{的用户创建一个临时A来进行聚合的初始化{1}}。因此clang在拒绝编译时是正确的。标准:

  

no virtual, private, or protected (since C++17) base classes

不过,按照标准说明使用dtor即可。

基地的()可以是公共的或受保护的。

  

一个常见的指导原则是,必须将基类的析构函数作为   公共和虚拟或受保护和非虚拟

see the guideline of standard

与C ++ 11相反,在C ++中,表达式dtorB(),而表达式prvalue是移动构造,并且该移动可能会被忽略。 17,没有动静。 auto b = B();未被移走。这是prvalue的值初始化,并且完全等同于:

B()

Value Categories in C++17

Should this code fail to compile in C++17?

答案 1 :(得分:1)

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

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

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

<块引用>

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

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

<块引用>

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