Двойная рассылка и шаблон класса

У меня есть код 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*, потому что это будет виртуальный и шаблон.

Вопрос: есть ли аккуратный способ преодолеть эту проблему?

6

Решение

На самом деле нет решения этой проблемы: вам нужно
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, тем не мение.
Если у вас есть другие функции, которые затрагиваются, вы вернулись
к тому, что я изначально сказал.

3

Другие решения

Компилятор должен знать (конечный) набор isSameSpecific виртуалы в то время, когда он анализирует class Foo определение. Все виртуалы имеют зарезервированные записи в виртуальной таблице. Шаблон Qux может быть отменено неограниченное количество раз, что требует неограниченного количества виртуальных Foo, Понятно, что это не может работать, даже не пытаясь описать метод определения их всех.

Вы, вероятно, можете использовать typeinfo, чтобы делать то, что вы хотите, но это не будет с полиморфизмом типов.

1

Вы правы, что это двойная отправка, и вы правы, что, к сожалению, метод не может быть одновременно 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;

1

Как насчет использования 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 переопределения. Это может быть немного безопаснее.

1
По вопросам рекламы [email protected]