在下面的代码中,使用Clang 8.0.0+和-std=c++17
进行了编译,使用B{}
创建派生类实例会产生错误error: temporary of type 'A' has protected destructor
。当临时类型为A
(因此应该具有公共析构函数)时,为什么B
会出现在此消息中?
class A {
protected:
A() = default;
~A() = default;
};
class B : public A {
// can also omit these 3 lines with the same result
public:
B() = default;
~B() = default;
};
void foo(const B&) {}
int main() {
// error: temporary of type 'A' has protected destructor
foo(B{});
// ^
return 0;
}
答案 0 :(得分:11)
这是C ++ 20之前的aggregate initialization的一个微妙问题。
在C ++ 20之前,B
(和A
)为aggregate types:
(重点是我的)
没有用户提供的,继承的或显式的构造函数(明确允许使用默认或删除的构造函数)(自C ++ 17起)(直到C ++ 20)
然后
如果初始化程序子句的数量小于成员
and bases (since C++17)
的数量,或者初始化程序列表完全为空,则根据以下说明,由空列表初始化and bases (since C++17)
的其余成员by their default member initializers, if provided in the class definition, and otherwise (since C++14)
通常的列表初始化规则(该规则会使用默认构造函数对非类类型和非聚合类执行值初始化,并对聚合进行聚合初始化)。
因此B{}
通过聚合初始化构造了一个临时对象,这将直接用空列表初始化基础子对象,即执行聚合初始化以构造A
基础子对象。请注意,B
的构造函数被绕过。问题在于,在这种情况下,无法调用protected
分解器来破坏直接构建的A
类型的基础子对象。 (它没有抱怨protected
构造函数,因为A
的聚合初始化也绕过了它。)
您可以将其更改为foo(B());
以避免聚合初始化; B()
执行value-initialization,临时对象将由B
的构造函数初始化,那么一切都很好。
顺便说一句,从C ++ 20开始,您的代码可以正常工作。
没有用户声明或继承的构造函数(自C ++ 20起)
B
(和A
)不再是聚合类型。 B{}
执行list initialization,然后由B
的构造函数初始化该临时对象;效果与B()
相同。