继承的构造函数忽略类内初始化

Inherited constructors ignore in-class initialization

本文关键字:初始化 构造函数 继承      更新时间:2023-10-16

我有一个定义如下的类:

class ASTConcatenateLiteral : public ASTExpr {
using ASTExpr::ASTExpr;
private:
Type type_ = Type::nothingness();  // Type does not have a default constructor
};

这适用于 Clang。但是,GCC 给了我一条错误消息,让我认为它正在尝试使用默认的初始值设定项:

错误:调用"表情符号代码编译器::类型::类型(("没有匹配函数

如果我用这样的公共构造函数替换using ASTExpr::ASTExpr;(ASTExpr 只提供这个构造函数(

ASTConcatenateLiteral(const SourcePosition &p) : ASTExpr(p) {}

一切正常。

根据 cppreference.com:

继承的构造函数等效于具有空主体和成员初始值设定项列表的用户定义构造函数,该列表由单个嵌套名称说明符组成,该说明符将其所有参数转发给基类构造函数。

那么为什么继承的构造函数不起作用呢?如果继承的构造函数的行为类似于用户定义的构造函数,则它应该使用为type_提供的值,对吗?根据标准,哪个编译器是正确的?

Clang 是正确的。使用继承的构造函数时,初始化应像使用默认构造函数初始化派生类的对象一样进行,因此应使用默认成员初始值设定项。

(强调我的(

如果重载解析在初始化此类派生类的对象时选择继承的构造函数之一,则使用继承的构造函数初始化从中继承构造函数的 Base 子对象,并且 Derived 的所有其他基和成员都像默认的默认构造函数一样进行初始化(如果提供,则使用默认成员初始值设定项,否则将进行默认初始化(。

以下是标准中的一个示例:

struct B1 {
B1(int, ...) { }
};
struct B2 {
B2(double) { }
};
int get();
struct D1 : B1 {
using B1::B1;     // inherits B1(int, ...)
int x;
int y = get();
};
void test() {
D1 d(2, 3, 4);    // OK: B1 is initialized by calling B1(2, 3, 4),
// then d.x is default-initialized (no initialization is performed),
// then d.y is initialized by calling get()
D1 e;             // error: D1 has a deleted default constructor
}

请注意,d.y由默认成员初始值设定项初始化。

GCC开发人员认为这是一个编译器错误,PR67054,在GCC 7.2中得到了修复。

他们的最小例子是

struct A
{
A(int) {}
};
struct C
{
C(int) {}
};
struct B : A
{
using A::A;
C c = 42;
};
int main()
{
B b = 24;
}

在线编译器可用于验证 GCC 和 clang 现在是否一致。

请注意,据我所知,在GCC 7.1中,不会生成错误的代码。尽管编译器需要默认构造函数,但永远不会调用该默认构造函数。因此,旧版本的 GCC 的可能解决方法是提供默认构造函数的声明,而不是定义。甚至可以使用属性(不是标准C++(来拒绝任何使用它的尝试:

struct C
{
#ifdef GCC_WORKAROUND_PR67054
C() __attribute__((__error__("No.")));
#endif
C(int) {}
};