Шаблоны не могут быть "виртуальными"

Учитывая приведенный ниже код, компилятор показывает сообщение, указывающее, что 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&).

Ответ 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;
};