【问题标题】:Strange default empty constructor on a virtual inheritance behaviour on GCCGCC上虚拟继承行为的奇怪默认空构造函数
【发布时间】:2021-03-24 23:49:15
【问题描述】:

我的代码中存在以下情况,即派生类对基类具有虚拟继承:

class Base {
    int x;
public:
    Base(int x): x{x} {}
    virtual void f() = 0;
};

class Derived : public virtual Base  {
  public:
    Derived() = default;
};

class Concrete: public Derived {
public:
    Concrete(): Base{42} {}
    void f() override {}
};

链接:https://godbolt.org/z/bn1EY6

GCC (trunk) 给出以下错误:error: use of deleted function 'Derived::Derived()' 而 Clang (trunk) 编译它没有问题。

如果我将构造函数更改为 Derived() {} 而不是 Derived() = default 或在 Base 类上定义一个空的构造函数,则 GCC 可以工作。

为什么= default 在这种情况下会删除 GCC 中的函数?

【问题讨论】:

标签: c++ gcc clang virtual-inheritance


【解决方案1】:

为什么在这种情况下 = 默认会删除 GCC 中的函数?

这是否是 GCC 中的错误(MSVC 的行为类似,但 clang-cl 按原样接受代码)对于那些在 C++ 标准中进行更多研究的人来说是一个问题。但是,编译器似乎正在使用 = default 来暗示 Derived 构造函数依赖于(或等效于Base 的默认构造函数 - 绝对是 删除,因为您定义了另一个(非默认)构造函数。

但是,显式添加 您自己的 默认构造函数,使用 Derived() {} 删除隐含的依赖关系。

通过指定(即取消删除)Base 类的默认构造函数来确认(在 GCC 和 MSVC 中):

class Base {
    int x;
public:
    Base() : x{0} {}  // Adding this removes the error!
//  Base() = default; // Also works
    Base(int x): x{x} {}
    virtual void f() = 0;
};

class Derived : public virtual Base  {
  public:
    Derived() = default;
};

class Concrete: public Derived {
public:
    Concrete(): Base{42} {}
    void f() override {}
};

编辑:这也可能是相关的,甚至可能是重复的:Why is Default constructor called in virtual inheritance?

【讨论】:

    【解决方案2】:

    标准说(最新草案):

    [class.default.ctor]

    如果满足以下条件,则将类 X 的默认默认构造函数定义为已删除:

    • X 是一个联合 ... [[不适用]]
    • X 是一个非联合类,它有一个变体成员 M 与 ... [[不适用]]
    • 任何没有默认成员初始值设定项 ([class.mem]) 的非静态数据成员都是引用类型,[[不适用]]
    • 任何 const 限定类型的非变体非静态数据成员 ... [[不适用]]
    • X 是一个联合并且... [[不适用]]
    • X 是一个非联合类和任何匿名联合成员的所有成员... [[不适用]]
    • [应用如果基础是潜在构造的子对象]任何可能构造的子对象,除了带有大括号或相等初始化器的非静态数据成员,都有类M型(或 数组)并且 M 没有默认构造函数或重载 用于查找 M 对应的分辨率 ([over.match]) 构造函数导致歧义或函数被删除 或无法从默认的默认构造函数中访问,或
    • 任何可能构造的子对象都有一个带有析构函数的类型,该析构函数已从默认的默认构造函数中删除或无法访问。 [[不适用]]

    只有一个规则可能适用于被删除的默认默认构造函数,它取决于基础是否是可能构造的子对象

    [特殊]

    对于一个类,它的非静态数据成员,它的非虚拟直接基类,以及,如果类不是抽象的([class.abstract]),它的虚拟基类是调用它可能构造的子对象。

    Derived 是抽象的(因为它没有实现所有纯虚函数),而Base 是一个虚拟基础,因此基础不是可能构造的子对象,因此唯一适用于被删除的默认构造函数的规则不适用,因此不应删除。编译器错误。


    一个简单的解决方法(除了您已经提到的那些)是根本不声明Derived::Derieved()。在这种情况下,它似乎是正确隐式生成的。


    添加 noexcept 会产生错误内部编译器错误

    这也是一个编译器错误。

    【讨论】:

    猜你喜欢
    • 2018-04-18
    • 2021-06-02
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 2015-06-21
    • 2012-04-12
    • 1970-01-01
    相关资源
    最近更新 更多