У меня есть код C ++, где я сравниваю другой класс, производный от общего материнского класса, Foo
, Если два класса имеют не один и тот же тип, сравнение всегда false
, В противном случае он сравнивает некоторые внутренние данные, специфичные для данного класса.
Мой код выглядит так:
class Bar;
class Baz;
class Foo
{
public:
virtual bool isSame( Foo* ) = 0;
virtual bool isSameSpecific( Bar* ){ return false; }
virtual bool isSameSpecific( Baz* ){ return false; }
};
class Bar : public Foo
{
public:
bool isSame( Foo* foo){ return foo->isSameSpecific(this); }
bool isSameSpecific( Bar* bar){ return bar->identifier == identifier; }
int identifier;
};
// and the same for Baz...
Это прекрасно работает (я думаю, что это двойная отправка), я могу сравнить Bar
а также Baz
только с указателями на Foo
,
Но теперь приходит проблема. Я должен добавить шаблон класса:
template< typename T>
class Qux : public Foo
{
//...
};
Проблема в том, что в Foo
Я не могу объявить метод isSameSpecific
за Qux*
, потому что это будет виртуальный и шаблон.
Вопрос: есть ли аккуратный способ преодолеть эту проблему?
На самом деле нет решения этой проблемы: вам нужно
isSameSpecific
функция для каждого экземпляра
шаблон, который вы используете. (Другими словами, в Foo
:
template <typename T>
virtual bool isSameSpecific( Qux<T>* );
незаконно, но:
virtual bool isSameSpecific( Qux<int>* );
virtual bool isSameSpecific( Qux<double>* );
// etc.
нет.)
Возможно, вам удастся избежать создания
QuxBase
и имея Qux<T>
вытекают из этого. Более вероятный,
это просто переместит проблему в QuxBase
, но если
isSameSpecific
не зависит от типа T
, например
поскольку вы можете определить некоторый канонический охватывающий тип, он может
быть выполнимым Не зная больше о Qux
а также
isSameSpecific
Сложно сказать. (Если
Qux<T>::isSameSpecific
должен всегда возвращаться false
если
типы инстанцирования разные, например, вы можете набрать
регистрироваться QuxBase::isSameSpecific
и вперед к другому
виртуальная функция, если типы идентичны.)
Обратите внимание, что подобные проблемы влияют на все альтернативные способы
реализация нескольких отправок, а также. В конце концов, ты
запрашивать отправку через открытый набор типов, что означает
потенциально бесконечное количество различных функций.
Просто чтобы быть ясно: я предполагаю, что ваш isSame
это просто
Например, и что фактические операции могут быть более сложными.
Фактический код, который вы показываете, явно попадает в то, что я предлагаю в
второй абзац; на самом деле, это может быть реализовано даже
без многократной отправки. Просто определите канонический тип «идентификатор», определите виртуальный getCanonicalIdentifier
функция и
использовать это в isSame
:
bool Foo::isSame( Foo const* other ) const
{
return getCanonicalIdentifier()
== other->getCanonicalIdentifier();
}
В этом отношении, если различные типы подразумевают, что isSame
возвращает false (часто бывает, если isSame
означает, что это выглядит
нравится), все, что вам не нужно двойной отправки также:
bool Foo::isSame( Foo const* other ) const
{
return typeid( *this ) == typeid( *other )
&& isSameSpecific( other );
}
Производная isSameSpecific
придется конвертировать тип
указатель, но так как они гарантированы, что это то же самое
как тип this
это простая и безопасная операция.
Наконец: если классы не имеют семантики значения (и
почти наверняка не должно, если полиморфизм вовлечен),
что-то простое, как:
bool Foo::isSame( Foo const* other ) const
{
return this == other;
}
может хватить.
Все это относится только что-то вроде isSame
, тем не мение.
Если у вас есть другие функции, которые затрагиваются, вы вернулись
к тому, что я изначально сказал.
Компилятор должен знать (конечный) набор isSameSpecific
виртуалы в то время, когда он анализирует class Foo
определение. Все виртуалы имеют зарезервированные записи в виртуальной таблице. Шаблон Qux
может быть отменено неограниченное количество раз, что требует неограниченного количества виртуальных Foo
, Понятно, что это не может работать, даже не пытаясь описать метод определения их всех.
Вы, вероятно, можете использовать typeinfo, чтобы делать то, что вы хотите, но это не будет с полиморфизмом типов.
Вы правы, что это двойная отправка, и вы правы, что, к сожалению, метод не может быть одновременно virtual
а также template
(последний является вопросом реализации).
Я боюсь, что нет возможности сделать это с чистый дизайн; Однако вы можете обмануть в Qux
,
template <typename T>
class Qux: public Foo {
virtual bool isSame( Foo* foo ) {
if (Qux* q = dynamic_cast<Qux*>(foo)) {
return *this == *q;
}
return false;
}
}; // class Qux
Конечно, dynamic_cast
немного обманывает (как и все броски к детям), но эй оно работает!
Нота Бене: isSame
методы должны быть наверное const
и возьми const
аргументы, ака virtual bool isSame(Foo const* foo) const;
Как насчет использования RTTI:
#include <typeinfo>
struct Foo
{
virtual ~Foo() { }
virtual bool compare_with_same(Foo const & rhs) = 0;
};
struct Bar : Foo
{
int thing;
virtual bool compare_with_same(Foo const & rhs)
{
assert(dynamic_cast<Bar const *>(&rhs) != nullptr);
return static_cast<Bar const &>(rhs).thing == thing;
}
}
bool operator==(Foo const & lhs Foo const & rhs)
{
return typeid(lhs) == typeid(rhs) && lhs.compare_with_same(rhs);
}
В качестве альтернативы вы можете положить typeid
код в каждом compare_with_same
переопределения. Это может быть немного безопаснее.