虚拟继承与非默认构造函数

Virtual inheritance vs. non-default constructors

本文关键字:默认 构造函数 继承 虚拟      更新时间:2023-10-16

此代码(至少)被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

问题:

  1. 如果代码确实无效,则究竟是如何实现的标准?AFAICT,10.4/2和1.8/4加在一起意味着B不能是派生最多的类的类型,因此从12.6.2/10我们知道,B永远不能调用A的构造函数。(章节编号适用于C++11。)

  2. 如果代码有效,编译器是否违反了标准要求存在他们不可能调用的构造函数?注意,他们不仅想从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)")。