8

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

PS 这最初是在 ICC 论坛上提出的,但由于不限于此编译器(并且没有详细信息),所以在此处发布。

4

3 回答 3

5

Clang 将错误显示为:

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

12.1/5 说“如果 [...] 任何 [...] 虚拟基类 [...] 具有类类型 M [...] 和 [... ] M 没有默认构造函数 [...]。”

于 2012-07-27T18:52:42.860 回答
3

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

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

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

您可以致电 try 向标准委员会提出可能的改进建议。

于 2012-07-27T19:06:19.770 回答
0

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

如果给定的非静态数据成员或基类不是由 mem-initializer-id 命名的(包括由于构造函数没有 ctor-initializer 而没有 mem-initializer-list 的情况),则

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

在我看来,无论类是虚拟基类,B' 的默认构造函数仍然由编译器合成,并且这样的默认构造函数不知道如何构造它的A基类(“实体是默认初始化的(8.5) ”)。

于 2012-07-27T18:58:04.213 回答