Как предотвратить некоторые полиморфные объекты в функции, принимающие параметр базового указателя
У меня есть иерархия классов 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)