jtb*_*des 10 c++ initialization access-levels language-lawyer c++17
在以下代码中,使用Clang 8.0.0+进行编译,并-std=c++17使用创建派生类实例,将B{}给出error 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;
}
Run Code Online (Sandbox Code Playgroud)
son*_*yao 11
这是C ++ 20之前的聚合初始化的一个细微问题。
在C ++ 20之前,B(和A)是聚合类型:
(强调我的)
没有用户提供的,继承的或显式的构造函数(允许使用显式默认或删除的构造函数)(自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()执行值初始化,临时对象将由B的构造函数初始化,然后一切都很好。
顺便说一句,由于C ++ 20,您的代码可以正常工作。
没有用户声明或继承的构造函数(自C ++ 20起)
B(和A)不再是聚合类型。B{}执行列表初始化,然后由B的构造函数初始化该临时对象;效果与相同B()。
| 归档时间: |
|
| 查看次数: |
3652 次 |
| 最近记录: |