I have a class hierarchy of BaseThing, with 2 child classes XThing and YThing.
I want to be able to match XThings to XThings and YThings, but I want to prevent YThings from being matched to YThings. I want a compile time error if I try and match a YThing to a YThing.
The attempt to use a virtual function, Match1, would prevent a YThing from trying to match anything, which is not what I want; a YThing could still be matched to an XThing. I'd accept the compromise of only allowing XThings to match to YThings, but not the other way round, if it was an acceptable solution to preventing YThings from matching to YThings.
The use of a friend function, Match2, as I've written it, doesn't work as the function accepts a XThing*, but is supplied a BaseThing*.
Is there any way to get around this other than using a cast? Or is there a better solution?
What I have tried:
enum ThingTypes { Base, X, Y };
class XThing;
class BaseThing
{
public:
static std::unique_ptr<BaseThing> Create(ThingTypes T, int vals);
BaseThing(int vals) : m_vals(vals)
{
}
int GetVals() const
{
return m_vals;
}
virtual bool Match1(const BaseThing* thing) const = 0;
friend bool Match2(const XThing* thing1, const BaseThing* thing2);
private:
int m_vals;
};
class XThing : public BaseThing
{
public:
XThing(int vals) : BaseThing(vals)
{
}
bool Match1(const BaseThing* thing) const
{
if (GetVals() == thing->GetVals())
{
return true;
}
return false;
}
};
class YThing : public BaseThing
{
public:
YThing(int vals) : BaseThing(vals)
{
}
bool Match1(const BaseThing* thing) const
{
throw std::runtime_error("This Match is not allowed");
}
};
std::unique_ptr<BaseThing> BaseThing::Create(ThingTypes T, int vals)
{
switch (T)
{
case X:
return std::unique_ptr<BaseThing>(new XThing(vals));
case Y:
return std::unique_ptr<BaseThing>(new YThing(vals));
default:
throw std::runtime_error("unknown Thing type " + to_string(X));
}
}
bool Match2(XThing& thing1, const BaseThing* thing2)
{
if (thing1.GetVals() == thing2->GetVals())
{
return true;
}
return false;
}
int main()
{
unique_ptr<BaseThing> b1 = BaseThing::Create(X, 1);
unique_ptr<BaseThing> b2 = BaseThing::Create(Y, 2);
unique_ptr<BaseThing> b3 = BaseThing::Create(X, 3);
unique_ptr<BaseThing> b4 = BaseThing::Create(Y, 4);
bool ret = false;
ret = b1->Match1(b2.get());
ret = b1->Match1(b3.get());
ret = b2->Match1(b3.get());
ret = b2->Match1(b4.get());
ret = Match2(b1.get(), b2.get());
ret = Match2(b1.get(), b3.get());
ret = Match2(b2.get(), b3.get());
ret = Match2(b2.get(), b4.get());
return 0;
}