Иметь базовый класс A и производный класс B, который переопределяет шаблон функции Func:

class A
{
          A() {...};
          ~A() {};

          template <class T>
          void Func(const String &sInput, T &tResult)
          {...}
};

class B : public A
{
          B() {...}
          ~B() {};

          template <class T>
          void Func(const String &sInput, T &tResult)
          {...}
};

(Обратите внимание, что Func не является виртуальным, учитывая отсутствие поддержки в C++ шаблонных виртуальных функций.)

Теперь есть mainprog API, класс M:

class M
{
  M(boost::shared_ptr<A> &pInterfaceInput): pInterface(pInterfaceInput)
  {}  

  template <class T>
  Evaluate(const String &sInput, T &tResult) 
  {
    pInterface->Func<T>(sInput, tResult);
  }

  private:
  const boost::shared_ptr<A> pInterface;
};

Я хочу, чтобы функция Evaluate здесь поддерживала вызовы функций базового класса A или любого из его производных классов (например, B). Этот класс был написан с учетом полиморфизма до того, как я перепроектировал классы A и B, чтобы они имели шаблонные функции.

Теперь проблема заключается в том, что если я передам общий указатель базового типа производному типу, тогда будет вызван Func базового класса, а не производный класс.

Как мне обойти отсутствие динамического полиморфизма здесь? Я решил сделать класс M шаблоном класса для общего типа указателя и иметь static_cast в конструкторе, чтобы гарантировать, что этот тип относится к типу базового класса (A) или к производному классу.

Как лучше всего это сделать? Я бы предпочел не изменять классы A и B, чтобы обойти эту проблему, но все предложения приветствуются.

Спасибо.

2
paradoxx 6 Сен 2009 в 15:56

2 ответа

Похоже на проблему двойной отправки. Возможно, это подходящее место для реализации шаблона посетителя?

Например, создайте класс Evaluator и для каждого T подкласс ConcreteEvaluator<T>. Укажите методы A и B, которые посещают Evaluator. Что-то типа:

class Evaluator 
{
    virtual void visit_A(A* object);
    virtual void visit_B(B* object);
};

template <typename T>
class ConcreteEvaluator : public Evaluator
{
    public:
    String* input_reference;
    T& result_reference;

    ConcreteEvaluator(String& input_reference_,T& result_reference_) :
        input_reference(input_reference_), 
        result_reference(result_reference_) {}

    virtual void visit_A(A* object) {
        object->Func(input_reference,result_reference);
    }
    virtual void visit_B(B* object) {
        object->Func(input_reference,result_reference);
    }
 }

class A
{
...
virtual void apply_evaluator(Evaluator *eval) {eval->visit_A(this);}
...
}

class B
{
...
virtual void apply_evaluator(Evaluator *eval) {eval->visit_B(this);}
...
}

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

С другой стороны, это может быть полным излишеством. Приблизительно за тот же объем работы вы всегда можете просто заплатить за обновление M::Evaluate:

class M
{
    ...
    void Evaluate(const String& sInput, T& tResult)
    {
        // try to downcast to each subclass of A.  Be sure to check
        // sub-subclasses first
        try
        {
            dynamic_cast<B*>(pInterface.get())->Func(sInput, tResult);
            return;
        }
        catch (std::bad_cast& ) { }
        ...
        // nothing worked.  It must really be an A
        pInterface->Func(sInput,tResult);
    }
    ...
};
3
Managu 6 Сен 2009 в 17:30

В вопросе Templatized Virtual function я показал, как использовать стирание типа, чтобы получить некоторые эффектов виртуальной функции-члена. В зависимости от того, что вы хотите сделать в Func(), вы можете использовать здесь ту же технику.

1
Community 23 Май 2017 в 15:04