Иметь базовый класс 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 ответа
Похоже на проблему двойной отправки. Возможно, это подходящее место для реализации шаблона посетителя?
Например, создайте класс 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);
}
...
};
В вопросе Templatized Virtual function я показал, как использовать стирание типа, чтобы получить некоторые эффектов виртуальной функции-члена. В зависимости от того, что вы хотите сделать в Func(), вы можете использовать здесь ту же технику.
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C++ — это язык программирования общего назначения. Изначально он разрабатывался как расширение C и имел аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде, который будет скомпилирован с помощью компилятора C++. Используйте тег версии для вопросов, связанных с конкретной стандартной версией [C++11], [C++14], [C++17], [C++20] или [C++23]. и т.д.