【问题标题】:Inherited constructors ignore in-class initialization继承的构造函数忽略类内初始化
【发布时间】:2018-01-30 11:08:03
【问题描述】:

我有一个这样定义的类:

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_ 提供的值,对吗?根据标准,哪个编译器是正确的?

【问题讨论】:

  • 这可以用 g++ 7.2 编译。
  • @Holt Sheesh。难怪我没有设法重现这个问题,如果它只在旧版本中失败。
  • @Holt 我尝试使用 6.2.0。但在那种情况下,我将不得不再次提高 GCC 版本要求......
  • error: no matching function for call to ‘EmojicodeCompiler::Type::Type()’ 这应该穿在极客 T 恤上。

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


【解决方案1】:

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是由默认成员初始化器初始化的。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2021-04-29
      • 2014-10-23
      • 1970-01-01
      • 2014-04-02
      • 1970-01-01
      • 2014-04-05
      • 1970-01-01
      • 2013-12-13
      • 2017-04-17
      相关资源
      最近更新 更多