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

时间:2017-08-22 08:46:58

标签: c++ c++11 gcc clang language-lawyer

我有一个像这样定义的类:

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

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

  

错误:没有匹配函数来调用'EmojicodeCompiler :: Type :: Type()'

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

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

一切正常。

根据cppreference.com

  

继承的构造函数等效于用户定义的构造函数,它们具有一个空体,并且具有一个成员初始化列表,该列表由一个嵌套名称说明符组成,它将所有参数转发给基类构造函数。

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

2 个答案:

答案 0 :(得分:15)

Clang是对的。当使用inherited constructor时,初始化应该继续进行,就像使用默认的默认构造函数初始化派生类的对象一样,因此应该使用default member initializers

(强调我的)

  

如果重载决策在初始化此类派生类的对象时选择其中一个继承的构造函数,则使用继承的构造函数初始化从中继承构造函数的Base子对象,并初始化Derived的所有其他基础和成员,就好像通过默认的默认构造函数(如果提供,则使用默认成员初始值设定项,否则进行默认初始化)。

以下是standard

的示例
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由默认成员初始化程序初始化。

答案 1 :(得分:12)

GCC开发人员将其视为编译器错误PR67054,已在GCC 7.2中修复。

Their minimal example

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) {}
};