views:

1099

answers:

2

Imagine a standard diamond inheritance. Class A defines pure virtual function fx, class B defines implementation for fx, classes C and D do nothing with fx. When trying to call fx on instance of class D you'll get 'ambiguous function call' error although there is only one implementation of fx. This can be solved by B and C inheriting from A in virtual manner. Is it a correct solution for the problem? How exacly does virtual inheritance handle merging of virtual function tables?

A--->B--->D

\--->C------^

Thanks! Martin

+2  A: 

Is it a correct solution for the problem?

"Diamond" inheritance is problematic, and the correct explanation of the solution takes a bit of explaining. I recommend you read the following chapters of Meyers' Effective C++:

  • Item 26, Guard against potential ambiguity
  • Item 43, Use multiple inheritance judiciously.
ChrisW
+8  A: 

... Note, Herb Sutter wrote 3 excellent articles about multiple inheritance (1) here, (2) here and (3) here . He wrote a whole other bunch of useful articles in guru-of-the-week here. Highly recommended ...

First, i'm not sure that i get your hierarchy right. I take it it is like this:

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

struct B : A { void F() { } };
struct C : A { };
struct D : B, C { };

Well, D is abstract, because there are two A subobjects in an object of type D: One that is made concrete by B through the lattice of B, and one that is still abstract in the lattice going through C. I take it you have a pointer to D and try to call F. Yes, an ambiguity arises, because the compiler finds two functions F in two separate lattices:

D -> B::F
D -> C -> A::F

Looking like this:

    F()   F()
     A     A
     |     |
 F() B     C
      \   /
        D

You can fix that situation formally by deriving from A virtually:

struct B : virtual A { void F() { } };
struct C : virtual A { };
struct D : B, C { };

You then have this situation, called diamond inheritance:

       F()  
        A
      /   \
 F() B     C
      \   /
        D

And doing the lookup, it finds that there is B::F overriding A::F. Although A::F can still be reached through D::C::A, that is not an ambiguity anymore, because A was inherited virtual.

Whether this is the correct solution in your particular problem - that's of course not certain. There are most often better ways than deriving virtual from a class. To your question about merging of virtual function tables - that's completely depending on the implementation. GCC, as far as i know, will keep a pointer to the one A instance in the virtual table of D, if we derive virtual.

Johannes Schaub - litb
Thanks. This is what I've asked about. If the functionality is compiler-implementation-dependent it's no way to go.
the behavior is the same for every compiler - it's the way compilers achieve it that is different. You can trust compilers that they behave like each other, as long as you don't depend on a particular layout of the object (like, the sizeof value) that would depend on an implementation.
Johannes Schaub - litb
Ok, let me ask more precisely: If there's diamond virtual inheritance and there is a function defined in both A and B, but not in C, can I access the function via D? And which one would that be, the one defined in A or the one from B? Is the behaviour consistent for all compilers?
B is called, because it overrides the function F in A. as long as there is no function F in C (which would introduce an ambiguity then - there is no unique final overrider for A::F), you are fine calling it on a D object and the one in B is called, for all conforming compilers of course.
Johannes Schaub - litb