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