在使用Clang 8.0.0+和-std=c++17
编译的以下代码中,使用B{}
创建派生类实例会产生错误error: temporary of type 'A' has protected destructor
。当临时的类型为B
(因此应该有一个公共析构函数)时,为什么A
会出现在此消息中?
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;
}
发布于 2019-06-25 08:44:43
这是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()
一样。
https://stackoverflow.com/questions/56745324
复制相似问题