templates may not be ‘virtual’

2020-08-09 11:37发布

问题:

Given the code below, the compiler is showing a message pointing that error: templates may not be ‘virtual’. Does anyone have a suggestion on how to solve the bug?

template < class FOO_TYPE>
class CFoo{
    public:
        ...
        template < class BAR_TYPE >
        virtual void doSomething( const CBar<BAR_TYPE> &); // here's 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:

The easiest reason to see why this is illegal is by considering the vtable. Sure, that's just one common implementation, and others are allowed. But all virtual functions in C++ are designed such that they can be implemented with a vtable.

Now, how many entries are there in the vtable of CFoo<int> ? Is there an entry for doSomething<float> ? And doSomething<float*>? And doSomething<float**> ? Templates such as these allow an infinite set of functions to be generated. Usually that's no problem, as you use only a finite subset, but for virtual functions this subset isn't known, and therefore the vtable would need to be infinite.

Now, it's possible that you really wanted only a single entry in the vtable. In that case, you'd write it as follows:

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;
};

This means that the vtable for CFoo<int, float> will have one entry, for doSomething(float const&).



回答2:

You can use what we call in Symbian as "template design pattern". Here is sample code to give you an idea:

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;
}; 


回答3:

If you really need to make this method virtual, consider making CBar<> polymorphic and pass a base type in which isn't templated.

EDIT: something like this:

// 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:

Well, the error message is pretty clear. Member function templates can't be virtual. How to solve this depends on your problem, but the easiest thing to do would be to make the member functions non-virtual and reconsider your design.