Search code examples
c++g++multiple-inheritance

What is the way to reach a field or a method of a far away base class in a double diamond?


In the "classical" diamond problem like in the following (one where there is no virtual in front of public D, behind class C and class B), one may resolve the ambiguity using the namescope operator ::, such as in the constructor of class A:

/* 
 *   D                            D   D
 *  / \   which without 'virtual' |   |
 * B   C      is actually:        B   C
 *  \ /                            \ /
 *   A                              A
 */
#include <iostream>
using namespace std;
class D                      { public: char d = 'D';};
class C : public D           { public: char c = 'C';};
class B : public D           { public: char b = 'B';};
class A : public B, public C { public: char a = 'A'; A();};

A::A() {
    cout << B::d; //This works! B's d, inherited from D.
    cout << C::d; //This works! C's d, inherited from D.
    //cout << D::d;     //This doesn't work (ambiguous)
    //cout << B::D::d;  //Doesn't work either though.
    //cout << C::D::d;  //Doesn't work either though.
}

int main() {
    A a;
    cout << endl;
    return 0;
}

Consider a double diamond such as this now:

/* 
 *   G                          G  G G  G
 *  / \                         |  | |  |
 * E   F                        E  F E  F
 *  \ /                          \ / \ /
 *   D                            D   D
 *  / \   which without 'virtual' |   |
 * B   C      is actually:        B   C
 *  \ /                            \ /
 *   A                              A
 */
#include <iostream>
using namespace std;
class G                      { public: char g = 'G';};
class E : public G           { public: char e = 'E';};
class F : public G           { public: char f = 'F';};
class D : public E, public F { public: char d = 'D';};
class C : public D           { public: char c = 'C';};
class B : public D           { public: char b = 'B';};
class A : public B, public C { public: char a = 'A'; A();};

A::A() {
    cout << /* How do I reach any of the two e's or f's 
               or any of the four g's?*/
}

int main() {
    A a;
    cout << endl;
    return 0;
}

How exactly would one reach the fields inherited from E, F and G? What actually seemed most logical to me was the following.

cout << B::D::d;
cout << B::D::E::e; 
cout << B::D::F::f; 
cout << B::D::E::G::g; 
cout << B::D::F::G::g; 

cout << C::D::d;
cout << C::D::E::e; 
cout << C::D::F::f; 
cout << C::D::E::G::g; 
cout << C::D::F::G::g; 

However (using g++) they all yield an error of the form 'X' is an ambiguous base of 'A'..

Can somebody explain why this doesn't work and what's the proper way to do this? What am I missing?


Solution

  • The reason this doesn't work:

    cout << B::D::d;
    

    is because: the scope resolution is right-left associative; this is in a sense like (B::D) :: d, although parentheses aren't actually allowed here. So the qualified lookup B::D is resolved, and that finds the type D. There's only one type called D , there are not separate types A::D and B::D. The same type can be found in multiple scopes.

    Therefore you get the equivalent of D::d which is ambiguous since there are multiple paths to a base of type D .


    To get at the variable you want, you may have to use a series of casts, e.g.:

    cout << static_cast<G&>(static_cast<E&>(static_cast<D&>(static_cast<B&>(*this)))).g;
    

    In C-style syntax you can use ((G&)(E&)(D&)(B&)(*this)).g although that is dangerous as if you make a mistake in the class ordering you'll get a reinterpret_cast instead of a static_cast which can malfunction.

    Actually in this case you can omit the D step, since a B has a unique E base:

    cout << static_cast<G&>(static_cast<E&>(static_cast<B&>(*this))).g;
    

    or even:

    cout << static_cast<B&>(*this).E::g;
    

    since there is a unique E::g once we are at B.


    It is also possible to use dynamic_cast instead of static_cast, I'm open to comments about which one would be a better style :)