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 :)
Let's start with the error you're getting. 'X' is an ambiguous base of 'A'.
The compiler is telling you that there's an ambiguity/uncertainty in getting the parent of the object you've selected for std::cout. Without specifying abstract base classes, you're correct in the inheritance structure.
/*
* 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
*/
Now the problem is actually in what you're writing to standard out. here's what you've written:
cout << B::D::d;
cout << B::D::E::e;
cout << B::D::F::e; // When does F inherit from E and get member variable e?
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::e; // When does F inherit from E???
cout << C::D::E::G::g;
cout << C::D::F::G::g;
I have yet to test this with g++ (I'm using MSVC++), but this works:
A::A() {
std::cout << B::b
<< B::D::d
<< B::D::E::e
<< B::D::F::f
<< B::D::E::G::g << std::endl;
/* How do I reach any of the two e's or f's
or any of the four g's?*/
}
Cheers!
Edit: Here's the error you're facing in MSVC++
Here's some proof of it working in MSVC++":
Now in g++:
#include <iostream>
#include <stdio.h>
using namespace std;
class G { public: char g = 'G';};
class E : virtual public G { public: char e = 'E';};
class F : virtual public G { public: char f = 'F';};
class D : public E, public F { public: char d = 'D';};
class C : virtual public D { public: char c = 'C';};
class B : virtual public D { public: char b = 'B';};
class A : public B, public C { public: char a = 'A'; A();};
A::A() {
std::cout << A::a
<< A::B::b
<< A::B::D::d
<< A::B::D::E::e
<< A::B::D::F::f
<< A::B::D::E::G::g
<< std::endl;
}
int main() {
A a;
cout << endl;
return 0;
}
This should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With