C++ 虚拟继承与非默认构造函数
该代码被(至少)MSVC、ICC和GCC拒绝: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 ) { }
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
问题:
另外,这一问题最初是在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,它支持功能删除(无论如何,手动)。