Andy Walton Ответов: 0

Как предотвратить некоторые полиморфные объекты в функции, принимающие параметр базового указателя


У меня есть иерархия классов BaseThing, с 2 дочерними классами XThing и YThing.

Я хочу иметь возможность сопоставлять XThings с XThings и YThings, но я хочу предотвратить сопоставление YThings с YThings. Мне нужна Ошибка времени компиляции, если я попытаюсь сопоставить YThing с YThing.

Попытка использовать виртуальную функцию Match1 предотвратила бы попытку YThing сопоставить что-либо, а это не то, что я хочу; YThing все еще может быть сопоставлен с XThing. Я бы согласился на компромисс, разрешив только XThings совпадать с YThings, но не наоборот, если бы это было приемлемым решением для предотвращения совпадения YThings с YThings.

Использование функции friend, Match2, как я ее написал, не работает, так как функция принимает XThing*, но поставляется BaseThing*.

Есть ли какой-нибудь способ обойти это, кроме использования гипса? Или есть лучшее решение?

Что я уже пробовал:

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;
}

Peter_in_2780

Я действительно не думал об этом прямо, но как насчет того, чтобы просто реализовать вкусы соответствия, которые вы хотите в каждом из производных классов. Другими словами, не наследуйте совпадение. Таким образом, вы получите такой список функций:
X. Match (base) [или X. Match (X) и X. Match (Y)]
Y. Совпадение(X)
но не Y. совпадение(Y)

0 Ответов