6

当我尝试编译以下代码时:

class A {
public:
    A(int v) : virt(v) { }
    int virt;
    int getVirt(void) const { return virt; }
};

class B : private virtual A {
protected:
    B(int v) : A(v) { }
    using A::getVirt;
};

class C : public B, private virtual A {
protected:
    C(int v) : A(v), B(v) { }
    using A::getVirt;
};

class D : public C {
public:
    D(void) : C(3) { }
    using C::getVirt;
};

#include <iostream>

int main(int argc, char *argv[]) {
    D d;
    std::cout << "The number is: " << d.getVirt() << std::endl;

    return 0;
}

我收到关于 D 没有实例化 A 的错误;那是对的吗?如果虚拟基嵌入在层次结构中,所有派生类是否也需要虚拟地从该基派生,以便它们可以调用虚拟基的参数构造函数?

顺便说一句,这是 G++ 产生的错误:

Main.cpp: In constructor ‘D::D()’:
Main.cpp:22:18: error: no matching function for call to ‘A::A()’
Main.cpp:22:18: note: candidates are:
Main.cpp:3:5: note: A::A(int)
Main.cpp:3:5: note:   candidate expects 1 argument, 0 provided
Main.cpp:1:7: note: A::A(const A&)
Main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
4

2 回答 2

6

这与访问控制无关(至少主要不是)。相反,您必须了解虚拟基的工作原理:虚拟基子对象由派生最多的类初始化。由于您没有A在 的构造函数初始化列表中提及,D因此尝试了默认构造函数,但不存在。

要解决此问题,请A在以下位置正确初始化D

 D() : A(3), C(3) { }

当您说 时A(3),名称查找是根据 12.6.2/2 执行的:

mem-initializer-id中,在构造函数类的范围内查找初始的非限定标识符,如果在该范围内找不到,则在包含构造函数定义的范围内查找。

正如 Drew Dorman 正确指出的那样,您可以通过调用虚拟基类来强制直接路径,::A从而获得所需的访问权限。

于 2013-03-15T20:17:08.457 回答
1

正如 Kerrek SB 提到的,您需要A在构造函数中初始化D.

但是,您还必须使用范围运算符明确告诉编译器您不是A从其(私有)派生的上下文中访问的。

class D : public C {
public:
    D(void) : ::A(3), C(3) { }
//            ^^ Access this constructor from a global context
    using C::getVirt;
};

这也意味着您的构造函数必须是公共的,就像您的代码一样。

于 2013-03-15T20:48:10.267 回答