虚拟继承与非默认构造函数
Virtual inheritance vs. non-default constructors
此代码(至少)被MSVC、ICC和GCC拒绝:
class A {
public:
A( int ) { }
};
class B: virtual public A {
public:
//B(): A( -1 ) { } // uncomment to make it compilable
virtual void do_something() = 0;
};
class C: public B {
public:
C(): A( 1 ) { }
virtual void do_something() { }
};
int main() {
C c;
return 0;
}
基于
error : no default constructor exists for class "A"
class B: virtual public A {
^
detected during implicit generation of "B::B()" at line 14
问题:
如果代码确实无效,则究竟是如何实现的标准?AFAICT,10.4/2和1.8/4加在一起意味着B不能是派生最多的类的类型,因此从12.6.2/10我们知道,B永远不能调用A的构造函数。(章节编号适用于C++11。)
如果代码有效,编译器是否违反了标准要求存在他们不可能调用的构造函数?注意,他们不仅想从B::B()调用A::A(),而且想要在编译C::C()时执行(双重怪异)。
附言:这最初是在国际商会论坛上提出的,但由于不限于此编译器,所以发布在这里(没有详细信息)。
Clang将错误显示为:
error: call to implicitly-deleted default constructor of 'B'
C(): A( 1 ) { }
^
12.1/5表示"如果[…]任何[…]虚拟基类[…]具有类类型M[…]并且[…]M没有默认构造函数[…],则类X的默认默认构造函数被定义为已删除。"
我认为你试图从标准中可以找到的事实中推导出一个"定理",然后你希望标准承认这个"定理"的存在。标准并没有做到这一点。它并不努力寻找并包含所有可能的"定理",这些定理可以从标准文本中推导出来。
你的"定理"是完全有效的(除非我遗漏了什么)。您是对的,因为类B
是抽象的,所以这个类永远不能用作派生程度最高的类。这立即意味着类B
将永远不会有机会构造其虚拟基A
。这意味着从技术上讲,在B
中,编译器不应该关心A
或任何其他虚拟库中适当构造函数的可用性和/或可访问性。
但该标准根本不建立这种连接,也不想建立这种连接。它不以任何特殊的方式处理抽象类的构造函数。强加给此类构造函数的要求与非抽象类的要求相同。
你可以试着向标准委员会提出一个可能的改进建议。
看起来12.6.2/4禁止我这样做:
如果给定的非静态数据成员或基类不是由mem初始值设定项id(包括没有mem初始化器列表,因为构造函数没有ctor初始化器),然后
--如果实体是的非静态数据成员(可能cv限定)类类型(或其数组)或基类,以及实体类是非POD类,实体默认初始化(8.5)…
在我看来,不管类是虚拟基,B
的默认构造函数仍然是由编译器合成的,并且这种默认构造函数不知道如何构造其A
基("实体默认初始化(8.5)")。
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 初始化具有非默认构造函数的std::数组项的更好方法
- 具有默认模板类型的默认构造函数的类型推导
- 如何使用非默认构造函数实例化模板化类
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 声明没有默认构造函数的字段
- 没有默认构造函数作为模板参数的自定义比较器
- C++17 没有默认构造函数的地图放置(私有默认构造函数)
- 使用移动调用对等构造函数unique_ptr默认构造函数
- C++复制构造函数和默认构造函数
- 将向量从 N1 缩小到 N2 项,而不触发默认构造函数并仅使用 move 语义
- 为什么即使我调用参数化构造函数也会调用默认构造函数?
- 具有非默认构造函数的单例类
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 如何处理没有默认构造函数但在另一个构造函数中构造的对象?
- 在C++中使用默认构造函数初始化对象的不同方法
- 在没有默认构造函数的情况下创建的派生对象
- 强制使用默认构造函数对成员进行未初始化的声明
- 使用默认构造函数初始化对象的不同方法
- 创建类类型的动态分配数组,其中类不得具有默认构造函数