> Oh, the dreaded diamond inheritance. One tries his/her best to avoid such a calamity.
There is nothing to be scared of; there is no calamity.
Or else any programmer using
std::cout would have kept running into insurmountable programming problems.
A SingingAthlete is an Athlete, is a Singer and both Athlete and Singer are Persons. There is no better way to express that in code other than multiple inheritance with a virtual base class at the root.
> One way to solve it is to use virtual inheritance:
Multiple inheritance, virtual inheritance, diamond(!) inheritance which petrifies people - all these are completely irrelevant to the problem at hand.
> I've had Singer and Athlete for quite some weeks now, and added a lot of code.
> Just today I added SingingAthlete (which I never foresaw doing) and ran into this ambiguity
The classic Go4 visitor pattern creates a cyclic dependency. To be able to freely add new derived classes (of Person in this case), an acyclic form of the visitor patter should be used.
This is one way to implement an acyclic visitor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
|
#include <iostream>
#include <list>
struct Person {
struct visitor { virtual ~visitor() = default ; };
virtual ~Person() = default ;
virtual void accept( visitor* ) = 0;
};
struct Athlete : public virtual Person {
struct visitor
{
virtual ~visitor() = default ;
virtual void visit( Athlete* ) = 0 ;
};
virtual void accept( Person::visitor* ov ) override
{
auto av = dynamic_cast<Athlete::visitor*>(ov) ;
if(av) av->visit(this) ;
}
};
struct Singer : public virtual Person {
struct visitor
{
virtual ~visitor() = default ;
virtual void visit( Singer* ) = 0 ;
};
virtual void accept( Person::visitor* ov ) override
{
auto sv = dynamic_cast<Singer::visitor*>(ov) ;
if(sv) sv->visit(this) ;
}
};
struct SingingAthlete : public Athlete, public Singer {
struct visitor
{
virtual ~visitor() = default ;
virtual void visit( SingingAthlete* ) = 0 ;
};
virtual void accept( Person::visitor* ov ) override
{
auto sav = dynamic_cast<SingingAthlete::visitor*>(ov) ;
if(sav) sav->visit(this) ; // if visitor implements SingingAthlete::visitor
else // visit both Athlete::visitor and Singer::visitor
{
Athlete::accept(ov) ;
Singer::accept(ov) ;
}
}
};
int main() {
struct RunningShoes : Person::visitor, Athlete::visitor, Singer::visitor {
virtual void visit( Athlete* ) override
{ std::cout << "Athlete puts on the running shoes.\n" ; }
virtual void visit( Singer* ) override
{ std::cout << "Singer has no need for the running shoes.\n" ; }
};
struct Microphone: Person::visitor, Athlete::visitor, Singer::visitor {
virtual void visit( Athlete*) override
{ std::cout << "Athlete has no need for the microphone.\n" ; }
virtual void visit( Singer* ) override
{ std::cout << "Singer sings with the microphone.\n" ; }
};
struct KaraokeWalkman : Person::visitor, SingingAthlete::visitor {
virtual void visit( SingingAthlete* ) override
{ std::cout << "SingingAthlete jogs with the KaraokeWalkman.\n" ; }
};
Person::visitor* objects[] = { new RunningShoes, new Microphone, new KaraokeWalkman };
std::list<Person*> people = { new Athlete, new Singer, new SingingAthlete };
for( Person* x: people )
{
for( auto object : objects ) x->accept(object) ;
std::cout << '\n' ;
}
}
|
http://coliru.stacked-crooked.com/a/83991cba9544747d