Pregunta

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?

¿Fue útil?

Solución

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

Otros consejos

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)

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();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top