Newbie me is currently stuck at this seemingly simple problem. Let's say I want to write some code about a bunch of animals procreating happily everafter. Obviously, they'd all need a mate() method, so I could define an abstract class like this:
class FemaleAnimal {
public:
virtual FemaleAnimal mate(const MaleAnimal& a) const = 0;
virtual MaleAnimal mate(const MaleAnimal& a) const = 0;
}
And derive all the different species:
class FemaleBird : public FemaleAnimal {
public:
FemaleBird mate (const MaleBird& b) const;
MaleBird mate (const MaleBird& b) const;
}
class FemaleBee: public FemaleAnimal {
public:
FemaleBee mate (const MaleBee& b) const;
MaleBee mate (const MaleBee& b) const;
}
In main(), I want two vectors
males = vector<MaleAnimal>
females = vector<FemaleAnimal>
each of which might contain both birds and bees, and is filled at run time. The species at each index match, so if males[i] is a bee, then females[i] is also a bee, so I can do
vector<FemaleAnimal> femaleOffspring;
vector<MaleAnimal> maleOffspring;
for (int i=0; i<males.size(); ++i){
femaleOffspring.push_back( females[i].mate(males[i]) );
maleOffspring.push_back( females[i].mate(males[i]) );
}
Now, obviously I want the mate() method in the derived classes to implement the one in the base class, but then I'd have to define mate() for animals in general, such as
FemaleBee::mate(const MaleAnimal& a) const;
But bees don't mate with birds. How would I achieve that kind of specialization? Is there some special design pattern for this? I've tried to look into things like covariance, but that was more confusing than helping.
Bonus question: How do I capture the case when males[i] and females[i] are of different species at runtime?
Edit: Assume that males and females come from entirely different class hierarchies and could not sensibly be derived from a common base class.
Edit: For completeness sake, here is the final solution based on n.m.'s answer. Lots of thanks everybody!
#include <iostream>
using namespace std;
class GenericMale {
public:
virtual void test() = 0; // just to make the class abstract
};
template <typename Species>
class Male : public GenericMale
{
void test() {};
};
class GenericFemale {
virtual void tryMate (const GenericMale&) const = 0;
};
template <typename Species>
class Female : public GenericFemale
{
public:
virtual void tryMate (const GenericMale& m) const
{
try {
auto& p= dynamic_cast<const Male<Species>&>(m); // will throw if Species does not match
Species::doMate(p);
} catch ( exception& e) {
cerr << "[MATING ERROR] You filthy animals, stay within your own species!" << endl;
}
}
};
class Bee {
public:
static void doMate(const Male<Bee>& p) {
cout << "Buzz buzz buzz!" <<endl;
}
};
class Bird {
public:
static void doMate(const Male<Bird>& p) {
cout << "Chirpy chirpy cheep cheep!" << endl;
}
};
int main() {
Female<Bee> queenBee;
Male<Bee> drone;
queenBee.tryMate(drone);
Female<Bird> mamaBird;
Male<Bird> papaBird;
mamaBird.tryMate(papaBird);
queenBee.tryMate(papaBird);
}