Click here to Skip to main content
15,867,568 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
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
	{
// terrible hack
		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());

// these lines do not compile
	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;
}
Posted
Comments
Peter_in_2780 2-Mar-17 17:07pm    
I haven't really thought this right through, but how about just implementing the flavours of Match you want in each of the derived classes. In other words, don't inherit Match. So you'd wind up with this sort of list of functions:
X.Match(base) [or X.Match(X) and X.Match(Y)]
Y.Match(X)
but NOT Y.Match(Y)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900