Как смоделировать виртуальность для шаблона метода - PullRequest
4 голосов
/ 30 сентября 2011

У меня есть иерархия классов, в которой я хочу представить шаблон метода, который бы вел себя как виртуальный.Например, простая иерархия:

class A {
  virtual ~A() {}

  template<typename T>
  void method(T &t) {}
};

class B : public A {
  template<typename T>
  void method(T &t) {}
};

Затем я создаю объект B:

A *a = new B();

Я знаю, что могу получить тип, сохраненный в a, по typeid(a).Как я могу назвать правильный B::method динамически, когда я знаю тип?Возможно, у меня может быть такое состояние:

if(typeid(*a)==typeid(B))
    static_cast<B*>(a)->method(params);

Но я бы хотел избежать подобных условий.Я думал о создании std::map с typeid в качестве ключа, но что бы я назвал значением?

Ответы [ 5 ]

6 голосов
/ 30 сентября 2011

Вы можете использовать «Любопытно повторяющийся шаблон» http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Используя этот шаблон, базовый класс принимает тип производного класса в качестве параметра шаблона, что означает, что базовый класс может привести себя к производному типу для вызова функций из производного класса. Это своего рода реализация виртуальных функций во время компиляции, с дополнительным преимуществом отсутствия необходимости выполнять вызов виртуальной функции.

template<typename DERIVED_TYPE>
class A {
public:
    virtual ~A() {}

    template<typename T>
    void method(T &t) { static_cast<DERIVED_TYPE &>(*this).methodImpl<T>(t); }
};

class B : public A<B>
{
friend class A<B>;

public:
    virtual ~B() {}

private:
    template<typename T>
    void methodImpl(T &t) {}
};

Затем его можно использовать так ...

int one = 1;
A<B> *a = new B();
a->method(one);
2 голосов
/ 30 сентября 2011

Есть ли какой-нибудь общий код, который вы можете извлечь и сделать виртуальным?

class A {
  virtual ~A() {}

  template<typename T>
  void method(T &t) 
  {
      ...
      DoSomeWork();
      ...
  }

  virtual void DoSomeWork() {}
};

class B : public A {
  virtual void DoSomeWork() {}
};
1 голос
/ 30 сентября 2011

Как вы, возможно, знаете, у вас не может быть шаблонов для виртуальных функций, поскольку все виртуальные функции являются частью типа класса и должны быть известны заранее.Это исключает любое простое «произвольное переопределение».

Если это опция, вы можете сделать параметр шаблона частью класса:

template <typename T> class A
{
protected:
  virtual void method(T &);
};

template <typename T> class B : public A<T>
{
  virtual void method(T &); // overrides
};

Более сложный подход может использовать некоторый объект диспетчера:

struct BaseDispatcher
{
  virtual ~BaseDispatcher() { }
  template <typename T> void call(T & t) { dynamic_cast<void*>(this)->method(t); }
};
struct ConcreteDispatcher : BaseDispatcher
{
  template <typename T> void method(T &);
};

class A
{
public:
  explicit A(BaseDispatcher * p = 0) : p_disp(p == 0 ? new BaseDispatcher : p) { }
  virtual ~A() { delete p_disp; };
private:
  BaseDispatcher * p_disp;
  template <typename T> void method(T & t) { p_disp->call(t); }
};

class B : public A
{
public:
  B() : A(new ConcreteDispatcher) { }
  // ...
};
0 голосов
/ 03 октября 2011

Oops .Сначала ответил на неправильный вопрос - ну, на другом вопросе

После некоторых размышлений я признал это классическим мульти-методом требование, то есть метод, который отправляет на основе тип времени выполнения более чем одного параметра.Обычные виртуальные функции для сравнения single dispatch (и отправляются только по типу this).

См. Следующее:

  • АндрейАлександреску написал (основополагающие биты для C ++?) По реализации мульти-методов с использованием обобщений в «Современном дизайне C ++»
    • Глава 11: «Мультиметоды» - он реализует базовые мульти-методы,сделав их логарифмическими (используя упорядоченные списки типов), а затем перейдя к мульти-методам с постоянным временем.Довольно мощная штука!
  • A статья codeproject , которая, кажется, имеет именно такую ​​реализацию:
    • без использования приведения типов любого типа (динамический, статический, переинтерпретированный, константный или в стиле C)
    • без использования RTTI;
    • без использования препроцессора;
    • сильный тип безопасности;
    • отдельная подборка;
    • постоянное время выполнения мультиметода;
    • нет динамического выделения памяти (через new или malloc) во время вызова с несколькими методами;
    • нет использования нестандартных библиотек;
    • используются только стандартные функции C ++.
  • Компилятор открытого метода C ++ , Питер Пиркельбауэр, Юрий Солодкий и Бьярн Страуструп
  • Библиотека Loki имеет MultipleDispatcher
  • В Википедии есть довольно приятная простая запись спримеры по множественной диспетчеризации в C ++.

Вот «простой» подход из статьи Википедии для справки (менее простой подход лучше масштабируется для большего числа производных типов):

// Example using run time type comparison via dynamic_cast

struct Thing {
    virtual void collideWith(Thing& other) = 0;
}

struct Asteroid : Thing {
    void collideWith(Thing& other) {
        // dynamic_cast to a pointer type returns NULL if the cast fails
        // (dynamic_cast to a reference type would throw an exception on failure)
        if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
            // handle Asteroid-Asteroid collision
        } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
            // handle Asteroid-Spaceship collision
        } else {
            // default collision handling here
        }
    }
}

struct Spaceship : Thing {
    void collideWith(Thing& other) {
        if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
            // handle Spaceship-Asteroid collision
        } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
            // handle Spaceship-Spaceship collision
        } else {
            // default collision handling here
        }
    }
}

0 голосов
/ 30 сентября 2011

Я думаю, что единственным решением является http://en.wikipedia.org/wiki/Visitor_pattern

См. Эту тему: Как добиться "функции виртуального шаблона" в C ++

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...