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

C++ 虚拟继承与非默认构造函数,c++,constructor,language-lawyer,virtual-inheritance,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 ) { }

该代码被(至少)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()时执行此操作(双精度)


  • 另外,这一问题最初是在ICC论坛上提出的,但由于不限于此编译器(未提供详细信息),因此发布在此处。

    Clang将错误显示为:

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

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

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

    如果给定的非静态数据成员或基类不是由 mem初始值设定项id(包括没有 mem初始值设定项列表,因为构造函数没有ctor初始值设定项), 然后

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


    在我看来,不管类是一个虚拟基,编译器仍然合成了
    B
    的默认构造函数,而这种默认构造函数不知道如何构造它的
    a
    基(“实体是默认初始化的(8.5)”。

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

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

    但该标准根本没有建立这种联系,也不愿意建立这种联系。它没有以任何特殊的方式处理抽象类的构造函数。对此类构造函数的要求与对非抽象类的要求相同


    您可以调用try,将其作为标准委员会的一项可能改进。

    +1,有趣的是,如果您深入到ABI级别,就会做出区别(以安腾ABI为例),可能会为一种类型生成三个构造函数:complete、complete allocating、base。基构造函数是唯一对虚拟基有意义的构造函数(因为它永远不可能是一个完整的对象),并且该构造函数不会调用虚拟基的构造函数。。。因此,从技术角度来看,这个问题中的假设是有意义的,这是一个关于定理证明的任意限制(我不是说应该改变标准来允许它,只是可以这样做)。我希望看到这种一致性。@Dribea在取消标记行的注释并检查B::B()的反汇编时,我可以看到类似“如果A\u应该从B中初始化,请调用A::A(int)”的可疑内容。看起来编译器更喜欢创建一个最通用的构造函数,并在运行时做出基本的初始化决定。因此,这两个代码都是错误的,编译器试图用B::B()做错误的事情(根据原始错误消息)。感谢您的参考,这似乎是我遗漏的一点。@vpozdyayev:是的,但这完全来自C++11,而您在原始帖子中引用的编译器/错误消息中,至少有一些充其量是C++03。@AndreyT这一点很好。OTOH,我引用的错误消息来自ICC12.1.5,它支持功能删除(无论如何,手动)。