Шаблоны не могут быть "виртуальными"
Учитывая приведенный ниже код, компилятор показывает сообщение, указывающее, что error: templates may not be ‘virtual’
. Есть ли у кого-нибудь предложение о том, как решить проблему?
template < class FOO_TYPE>
class CFoo{
public:
...
template < class BAR_TYPE >
virtual void doSomething( const CBar<BAR_TYPE> &); // here the error
...
virtual ~CFoo();
protected:
MyClass < FOO_TYPE > * m_pClass;
};
template < class FOO_TYPE >
template < class BAR_TYPE >
void CFoo<FOO_TYPE>::doSomething( const CBar<BAR_TYPE> & refBar ){
...
}
Ответы
Ответ 1
Простейшей причиной того, почему это незаконно, является рассмотрение vtable. Конечно, только одна общая реализация и другие разрешены. Но все функции virtual
в С++ разработаны таким образом, что их можно реализовать с помощью vtable.
Теперь, сколько записей в vtable
из CFoo<int>
? Есть ли запись для doSomething<float>
? И doSomething<float*>
? И doSomething<float**>
? Такие шаблоны позволяют создавать бесконечный набор функций. Обычно это не проблема, поскольку вы используете только конечное подмножество, но для виртуальных функций это подмножество неизвестно, и поэтому vtable должен быть бесконечным.
Теперь, возможно, вам действительно нужна только одна запись в vtable. В этом случае вы должны записать его следующим образом:
template < class FOO_TYPE, class BAR_TYPE>
class CFoo{
public:
...
virtual void doSomething( const CBar<BAR_TYPE> &); // now OK.
...
virtual ~CFoo();
protected:
MyClass < FOO_TYPE > * m_pClass;
};
Это означает, что vtable для CFoo<int, float>
будет иметь одну запись для doSomething(float const&)
.
Ответ 2
Ну, сообщение об ошибке довольно ясно. Шаблоны-члены-члены не могут быть виртуальными. Как решить это, зависит от вашей проблемы, но проще всего было бы сделать функции членов не виртуальными и пересмотреть ваш дизайн.
Ответ 3
Если вам действительно нужно сделать этот метод виртуальным, подумайте о том, чтобы сделать CBar<>
полиморфным и передать базовый тип, в котором не шаблон.
EDIT: что-то вроде этого:
// non-templated base class
class BarBase
{
// common methods go here..
};
template <typename BAR_TYPE>
class CBar : public BarBase
{
// implement methods from BarBase ...
};
template < class FOO_TYPE>
class CFoo{
public:
...
// now we take the base type, and this method does not need to be a template
virtual void doSomething( BarBase const* ptrBar );
...
virtual ~CFoo();
protected:
MyClass < FOO_TYPE > * m_pClass;
};
template < class FOO_TYPE >
void CFoo<FOO_TYPE>::doSomething( BarBase const* ptrBar ){
..
}
Ответ 4
Вы можете использовать то, что мы называем в Symbian, как шаблон шаблона шаблона. Вот пример кода, который даст вам представление:
class Base {
public:
virtual int DoSomething() = 0;
protected:
Base();
};
class IntermediateBase : public Base {
protected:
IntermediateBase(void* aSomeParam, void* aArg)
: iSomeParam(aSomeParam)
, iArgs(aArg)
{}
virtual int DoSomething() = 0;
protected:
void* iSomeParam;
void* iArgs;
};
template <class TYPE, class INPUT>
class ConcreteClass : public IntermediateBase {
typedef int (TYPE::*MemberFuncPtr)(const INPUT&);
public:
ConcreteClass(TYPE& aCommandType,
INPUT& aArgumentsToCommand,
MemberFuncPtr aMFP)
: IntermediateBase(static_cast<TYPE*>(&aCommandType),
static_cast<INPUT*>(&aArgumentsToCommand) )
, iMFP(aMFP)
{}
virtual int DoSomething() // VIRTUAL AND INLINE Note - dont make it
// virtual and inline in production if
// possible to avoid out-of-line copy
{
return static_cast<TYPE*>(iSomeParam)->*ConcreteClass::iMFP)
(*(static_cast<INPUT*>(iArgs));
}
private:
MemberFuncPtr iMFP;
};