I have a classic diamond problem like this in C++
A
/ \
B C
\ /
D
I know this would normally be solved by making B and C inherit virtually from A.
But my issue is that classes A and B come from a third party library I can't edit and B's inheritance from A is not marked virtual.
Is there a way to solve this?
Thanks for the help ;-)
If you can't change the inheritance of A in your library to virtual
, there is no way to make a diamond with a single A element a the top. The standard explicitly allows for mixing virtual and non-virtual inheritance of the same base class:
10.1/6: for an object c of class type C, a single subobject of type V is shared by every base subobject of c that has a virtual base class of type V. (...).
10.1/7: A class can have both virtual and non-virtual base classes of a given type.
Example:
namespace mylib { // namesape just to higlight the boundaries of the library
struct Person { // A
static int counter;
int id;
Person() : id(++counter) {}
void whoami() { cout << "I'm "<<id<<endl; }
}; //A
struct Friend: Person {}; //B -> A
int Person::counter=0;
}
struct Employee : virtual mylib::Person {}; // C->A
struct Colleague : Employee, mylib::Friend {}; // D->(B,c)
...
mylib::Friend p1; // ok !
p1.whoami();
Employee p2; // ok !
p2.whoami();
Colleague p3; // Attention: No diamond !
//p3.whoami(); // ouch !! not allowed: no diamond so for which base
// object has the function to be called ?
p3.Employee::whoami(); // first occurrence of A
p3.mylib::Friend::whoami(); // second second occurrence of A
As you have no way to intervene in your external library you have to organize things differently. But however you'll do it, it will be sweat and tears.
You could define C (Employee
in my example) by using composition of A (Person
in my example). The A subobject would either be created or in special cases taken over from another object. You'd need to undertake the effort to replicate A's interface, forwarding the calls to an A subobject.
The general idea would look like:
class Employee {
mylib::Person *a;
bool owna;
protected:
Employee (mylib::Person& x) : a(&x), owna(false) { } // use existing A
public:
Employee () : a(new mylib::Person), owna(true) { } // create A subobject
~Employee () { if (owna) delete a; }
void whoami() { a->whoami(); } // A - fowarding
};
If you do this, you could then define D with multiple inheritance, with a trick in the constructor:
struct Colleague : mylib::Friend, Employee {
Colleague () : mylib::Friend(), Employee(*static_cast<Person*>(this)) {};
using Friend::whoami;
};
The only issue would then the ambiguity of the member functions of A interface (that have been provided in C as explained above). You therefore have to tell with a using clause that for A, you go via B and not via C.
In final, you could use this:
Employee p2;
p2.whoami();
Colleague p3; // Artifical diamond !
p3.whoami(); // YES !!
p3.Employee::whoami(); // first occurence of A
p3.mylib::Friend::whoami(); // second second occurence of A
// all whoami refer to the same A !!!
It works nicely: Online demo
Conclusion
So yes, it's possible to solve this, but it's very tricky. As I said: it will be sweat and tears.
For instance, you have no problem to convert a Colleague
to a Person
. But for Employee
, you'd need to provide conversion operators. You have to implement the rule of 3/5 in Employee
And you have to take care of everything that could go wrong (failed allocation, etc...). It will not be a piece of cake.
So it's really worth to reconsider your design, as Lightness Races in Orbit suggested in the comments :-)