我的代码中有一个派生类与基类的虚拟继承的以下情况:
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(truck(编译它时没有问题。
如果我将构造函数更改为Derived() {}
而不是Derived() = default
,或者在基类上定义一个空构造函数,GCC就会工作。
为什么= default
在这种情况下会删除GCC中的功能?
标准说明(最新草案(:
[class.default.ctor]
类X的默认构造函数被定义为已删除,如果:
- X是一个并集[[不适用]]
- X是一个非并集类,它有一个带有[[不适用]]
- 任何没有默认成员初始值设定项([class.mem](的非静态数据成员都是引用类型,[[不适用]]
- const限定类型的任何非变体非静态数据成员[[不适用]]
- X是并集并且[[不适用]]
- X是一个非工会类,任何匿名工会成员的所有成员[[不适用]]
- [如果基是潜在构造的子对象,则应用]任何潜在构造的个子对象,除了具有大括号或相等初始值设定项的非静态数据成员外,都具有类类型M(或其数组(,并且M没有默认构造函数或重载应用于查找M对应的分辨率([over.match](构造函数导致歧义或函数被删除或无法从默认默认构造函数访问,或
- 任何潜在构造的子对象都有一个带析构函数的类型,该类型已从默认默认构造函数中删除或不可访问[[不适用]]
只有一条规则可能适用于要删除的默认构造函数,这取决于基是否是潜在构造的子对象。
[特殊]
对于一个类,其非静态数据成员,其非虚拟直接基类,并且,如果该类不是抽象的([class.abstract](,则其虚拟基类被称为其潜在构造的子对象。
Derived
是抽象的(因为它没有实现所有纯虚拟函数(,而Base
是一个虚拟基,因此该基是而不是一个潜在的构造子对象,因此,原本适用于要删除的默认构造函数的唯一规则不适用,因此不应删除它。编译器错误。
一个简单的解决方法(除了您已经提到的方法之外(是根本不声明Derived::Derieved()
。在这种情况下,它似乎是正确地隐式生成的。
添加noexcept会产生错误内部编译器错误
这也是一个编译器错误。
为什么在这种情况下=default会删除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 {}
};
EDIT:这也可能是相关的,甚至可能是重复的:为什么在虚拟继承中调用Default构造函数?