"temporary of type 'A' has protected destructor",但它的类型是B
"temporary of type 'A' has protected destructor", but its type is B
在以下代码中,使用 Clang 8.0.0+ 和 -std=c++17
编译,使用 B{}
创建派生的 class 实例会出现错误 error: temporary of type 'A' has protected destructor
。当临时对象的类型为 B
(因此应该有一个 public 析构函数)时,为什么 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;
}
这是 C++20 之前 aggregate initialization 的一个微妙问题。
在 C++20 之前,B
(和 A
)是 aggregate types:
(强调我的)
no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
然后
If the number of initializer clauses is less than the number of members and bases (since C++17)
or initializer list is completely empty, the remaining members and bases (since C++17)
are initialized by their default member initializers, if provided in the class definition, and otherwise (since C++14)
by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates).
所以B{}
通过聚合初始化构造一个临时对象,它将直接用空列表初始化基础子对象,即进行聚合初始化构造A
基础子对象。请注意, B
的构造函数被绕过了。问题是在这种情况下,无法调用 protected
析构函数来销毁直接构造的 A
类型的基类子对象。 (它不会抱怨 protected
构造函数,因为它也被 A
的聚合初始化绕过了。)
可以改为foo(B());
,避免聚合初始化; B()
执行value-initialization,临时对象会被B
的构造函数初始化,那就没问题了。
顺便说一句,自 C++20 以来,您的代码可以正常工作。
no user-declared or inherited constructors (since C++20)
B
(和A
)不再是聚合类型。 B{}
执行list initialization,然后临时对象被B
的构造函数初始化;效果与 B()
.
相同
在以下代码中,使用 Clang 8.0.0+ 和 -std=c++17
编译,使用 B{}
创建派生的 class 实例会出现错误 error: temporary of type 'A' has protected destructor
。当临时对象的类型为 B
(因此应该有一个 public 析构函数)时,为什么 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;
}
这是 C++20 之前 aggregate initialization 的一个微妙问题。
在 C++20 之前,B
(和 A
)是 aggregate types:
(强调我的)
no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
然后
If the number of initializer clauses is less than the number of members
and bases (since C++17)
or initializer list is completely empty, the remaining membersand bases (since C++17)
are initializedby their default member initializers, if provided in the class definition, and otherwise (since C++14)
by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates).
所以B{}
通过聚合初始化构造一个临时对象,它将直接用空列表初始化基础子对象,即进行聚合初始化构造A
基础子对象。请注意, B
的构造函数被绕过了。问题是在这种情况下,无法调用 protected
析构函数来销毁直接构造的 A
类型的基类子对象。 (它不会抱怨 protected
构造函数,因为它也被 A
的聚合初始化绕过了。)
可以改为foo(B());
,避免聚合初始化; B()
执行value-initialization,临时对象会被B
的构造函数初始化,那就没问题了。
顺便说一句,自 C++20 以来,您的代码可以正常工作。
no user-declared or inherited constructors (since C++20)
B
(和A
)不再是聚合类型。 B{}
执行list initialization,然后临时对象被B
的构造函数初始化;效果与 B()
.