为什么海湾合作委员会给我一个错误:没有独特的最终覆盖?

时间:2013-10-31 17:01:07

标签: c++ gcc diamond-problem

在下面的代码中,我收到以下警告和错误:

test.cpp:15: warning: direct base 'B' inaccessible in 'D' due to ambiguity
test.cpp:15: error: no unique final overrider for 'virtual void A::f()' in 'D'

但是如果我从A中移除B的虚拟继承(即struct B : public A),我只会得到警告,没有错误。

struct A
{
  virtual void f() = 0;
};

struct B : public virtual A
{
  void f() {}
};

class C : public B
{};

struct D : public C, virtual B
{};

int main()
{
  return 0;
}

为什么呢?这是可怕的钻石吗?

3 个答案:

答案 0 :(得分:3)

这是因为C以非虚拟方式从B继承,而D以虚拟方式从B继承。这会为您B两次,包括两次f()

B中尝试C的虚拟继承。

更新:那么为什么从B移除A中的虚拟继承时它会起作用?因为它改变了“最后的覆盖”。如果B中的AC中的B中没有虚拟,则会A两次:C一次(最后覆盖f()中的BB中的虚拟D中的f()B中的B的最后覆盖)。如果您将A中的虚拟继承添加回A,则f()将仅出现一次,并且将有两个最终覆盖竞争从{{实现纯A 1}},B,一次来自C,一次来自虚拟B

作为一种变通方法,您可以向D添加using,即using C::f;using B::f

参见C ++ 10.3 / 2

答案 1 :(得分:2)

让我们看看10.3[class.virtual]/2

中“最终覆盖者”的定义
  

类对象C::vf的虚拟成员函数S是最终覆盖,除非S是基类子对象(如果有)的最派生类声明或继承另一个覆盖vf

的成员函数      

在派生类中,如果基类子对象的虚拟成员函数具有多个最终覆盖,则程序格式不正确。

使用A的虚拟继承,只有一个类型为A的基类子对象,其虚拟成员函数f()具有多个最终覆盖(在B类的每个子对象中有一个)

如果没有来自A的虚拟继承,则有两个不同类型A的基类子对象,它们的虚拟成员函数f()每个都有自己的最终覆盖(每个B子对象中有一个)

答案 2 :(得分:0)

虚拟基础子对象在完整对象中的所有基础子对象之间“共享”。由于A是在D :: C :: B和D :: B之间共享,因此无法确定哪个B对象应该将其f()作为A::f().的覆盖

考虑:

#include <iostream>

struct A {
  virtual void f() = 0;
  virtual ~A() {}
};

struct B : virtual A
{
  void f() { std::cout << "B\n"; }
};

struct C : virtual A
{
  void f() { std::cout << "C\n"; }
};

struct D : C, B {};

int main() {
  D d;
  A *a = dynamic_cast<A*>(&d); // single shared A between B and C
  a->f(); // Should B::f() be called, or C::f()?
}

D中的B和C基础子对象共享相同的A基础子对象。当我们调用A :: f()时,将对覆盖函数进行虚拟查找。但是B和C都试图覆盖它,那么哪一个“赢”? x->f()是否打印“B”或“C”?答案是,进入这种情况的程序是不正确的。

当我们通过使B和C继承非虚拟来消除共享时,单独的A个基础子对象的每个子对象都被唯一的基类覆盖:

#include <iostream>

struct A {
  virtual void f() = 0;
  virtual ~A() {}
};

struct B : A
{
  void f() { std::cout << "B\n"; }
};

struct C : A
{
  void f() { std::cout << "C\n"; }
};

struct D : C, B {};

int main() {
  D d;
  // two different A objects
  A *a1 = static_cast<A*>(static_cast<B*>(&d));
  A *a2 =  static_cast<A*>(static_cast<C*>(&d));
  a1->f();
  a2->f();
}