此代码被(至少)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()时做(双奇怪)。
P.S。这最初是在ICC论坛上提出的,但由于不限于此编译器而在此处发布(并且没有详细说明)。
答案 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)”)。