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

时间:2012-07-27 18:22:33

标签: c++ constructor language-lawyer virtual-inheritance

此代码被(至少)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()时做(双奇怪)。

  3. P.S。这最初是在ICC论坛上提出的,但由于不限于此编译器而在此处发布(并且没有详细说明)。

3 个答案:

答案 0 :(得分:4)

Clang将错误显示为:

error: call to implicitly-deleted default constructor of 'B'
    C(): A( 1 ) {  }
    ^

12.1 / 5说“如果[...]任何[...]虚拟基类[...]具有类型M [...]和[],则将类X的默认默认构造函数定义为已删除。 ......] M没有默认构造函数[...]。“

答案 1 :(得分:3)

我认为你试图从标准中可以找到的事实中推导出一个“定理”,然后你期望标准承认“定理”的存在。标准不这样做。它并不努力寻找并纳入所有可能从标准文本中得出的“定理”。

你的“定理”是完全有效的(除非我遗漏了什么)。你是对的,因为类B是抽象的,所以这个类永远不能用作派生最多的类。这立即意味着类B永远不会有机会构建其虚拟基础A。这意味着在技术上B编译器不应该关心A或任何其他虚拟基础中适当构造函数的可用性和/或可访问性。

但是标准根本就没有建立这种联系,也不关心它。它不以任何特殊方式处理抽象类的构造函数。强加于此类构造函数的要求与非抽象类相同。

您可以打电话给我,建议将其作为标准委员会的可能改进。

答案 2 :(得分:0)

看起来12.6.2 / 4禁止我这样做:

  

如果给定的非静态数据成员或基类未由a命名   mem-initializer-id(包括没有的情况)   mem-initializer-list因为构造函数没有ctor-initializer),   然后

     

- 如果实体是非可能的非静态数据成员   cv-qualified)类类型(或其数组)或基类,以及   实体类是非POD类,该实体是默认初始化的   (8.5)...

在我看来,不管这个类是虚拟基类,B的默认构造函数仍由编译器合成,这样的默认构造函数不知道如何构造它的{{1} } base(“实体是默认初始化的(8.5)”)。