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?
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 :)