3

我需要一些帮助来解释这种现象:

#include <iostream>
using namespace std;

class A
{
public:
    void m() {cout<<"A::m "<<this<<endl;};
};

class B1:  public A
{
public:
    void m() {cout<<"B::m "<<this<<endl;};
};

class B2:  public A ,public B1
{
};

class D : public B2
{};

int main()
{
    B2 b;
    D d;
    A* a = &b; // Row 27
    //error: a = &d;  Base class 'A' is ambiguous // Row 28
    return 0;
}

为什么 Row27 中的代码有效,而 Row28 中的代码无效?预先感谢!

注意:我很了解虚拟继承,我只想知道 Row27 和 Row28 之间有什么区别 - 为什么一个抛出编译错误而另一个没有?

4

2 回答 2

1

因为您使用非虚拟继承从 派生A,这间接地A在每个类型的对象中创建了两个类型的子对象D。在进行从派生的指针到基址的转换时,编译器无法消除您引用的子对象的歧义,并发出错误。

为了只为 typeA的对象生成一个 type 的子对象D,您必须从Avirtual 沿继承路径进行继承,该继承路径使D派生自A

class B1:  virtual public A
{
    // ...
};

class B2:  virtual public A, public B1
{
};

编辑:

我尝试在 Visual Studio 2010 SP1 上编译您的示例,这给了我关于以下定义的警告B2

class B2: public A, public B1
{
};

1>sotest.cpp(18): warning C4584: 'B2' : base-class 'A' is already a base-class of 'B1'
1>          sotest.cpp(6) : see declaration of 'A'
1>          sotest.cpp(11) : see declaration of 'B1'

换句话说,出于某种原因,VC10 似乎考虑从A冗余继承并忽略它。这就是赋值A* a = &b;编译的原因:class B2实际上只继承A一次(通过B1)。情况并非如此D,因为 VC10 可能没有可忽略的冗余继承,并且D有效地从A两次(通过B1和通过B2)继承。

我忽略了 VC10 出现这种行为的原因,也不知道是否有编译器选项可以抑制这种行为。值得注意的是,GCC 4.7.2 和 Clang 3.2 都拒绝编译 assignment A* a = &b;

于 2013-01-21T17:44:03.643 回答
1

对于未来的用户来说,似乎:

我刚刚在 GCC 4.7.1 和 Clang 3.2 上尝试过,除非我使用虚拟继承,否则会出现编译器错误。你用的是什么编译器?– 安迪徘徊

VS2010 确实可以编译,但发出警告,表明它忽略了 B2 从 A 的继承。这就是第 27 行有效的原因。我认为这是 VS2010 的一种奇怪行为,我不知道是否有办法将其关闭(除了将所有警告视为错误) – Andy Prowl

谢谢@Andy Prowl。

于 2013-01-21T18:08:34.233 回答