4

In the code below, I get the following warning and error:

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'

But if I remove the virtual inheritance of B from A (i.e. struct B : public A), I only get the warning, no error.

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;
}

Why? Is this the dreaded diamond?

Rai
  • 1,328
  • 11
  • 20
  • possible duplicate of [Multiple inheritance + virtual function mess](http://stackoverflow.com/questions/616380/multiple-inheritance-virtual-function-mess) – Zeta Oct 31 '13 at 17:21

3 Answers3

3

It's because C inherits in a non-virtual way from B while D inherits in a virtual way from B. This gives you B two times including two f().

Try virtual inheritance of B in C.

Update: So why does it work when you remove the virtual inheritance in B from A? Because it changes the "final overrider". Without virtual in B from A and in C from B you have A two times: once in C (with the final override of f() in B) and once in the virtual B in D (with the final override of f() in B). If you add back the virtual inheritance in B to A, A will be present only once and there will be two final overrides competing to implement the pure f() from A, both in B, once from C and once from the virtual B.

As a workaround you could add a using to D, that is using C::f; or using B::f.

See C++ 10.3/2

Fozi
  • 4,973
  • 1
  • 32
  • 56
  • Virtually inheriting C from B does indeed solve the problem. But I still fail to see why removing the virtual inheritance of B from A would affect the outcome. – Rai Oct 31 '13 at 17:22
  • @Rai If you inherit virtually from `B` then you do not need the virtual inheritance from `A` any more. – Fozi Oct 31 '13 at 17:24
  • Yes, I believe that's true. But it still doesn't answer the question about why the error goes away when B no longer virtually inherits from A. – Rai Oct 31 '13 at 17:28
  • @Rai Ok, now I see where you are coming from. Please see my update. – Fozi Oct 31 '13 at 17:49
  • Ah now I see, thanks very much. You and Cubbi got it at about the same time. – Rai Oct 31 '13 at 17:52
2

Let's look at the definition of 'final overrider' from 10.3[class.virtual]/2

A virtual member function C::vf of a class object S is a final overrider unless the most derived class of which S is a base class subobject (if any) declares or inherits another member function that overrides vf.

In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.

With virtual inheritance from A, there is only one base class subobject of type A, and its virtual member function f() has more than one final overrider (one in each subobject of type B)

Without virtual inheritance from A, there are two different base class subobjects of type A, and their virtual member functions f() each has its own final overrider (one in each B subobject)

Community
  • 1
  • 1
Cubbi
  • 46,567
  • 13
  • 103
  • 169
0

Virtual base sub-objects are 'shared' between all base sub-objects in a complete object. Since A is is shared between D::C::B and D::B it can't tell which B object should have its f() called as the override for A::f().

Consider:

#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()?
}

The B and C base sub-objects in D both share the same A base sub-object. When we call A::f() a virtual look-up is done for the overriding function. But both B and C are trying to override it, so which one 'wins'? Does x->f() print "B" or "C"? The answer is that a program that gets into the situation is ill-formed.

When we eliminate the sharing by making B and C inherit non-virtually, then the separate A base sub-objects each have their functions overridden by unique base classes:

#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();
}
bames53
  • 86,085
  • 15
  • 179
  • 244