关于C++:模板可能不是”虚拟的”

templates may not be ‘virtual’

给定下面的代码,编译器将显示一条消息,指出error: templates may not be ‘virtual’。有人对如何解决这个bug有什么建议吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 ){
    ...
}


要想知道这是违法的最简单的原因是考虑vtable。当然,这只是一个常见的实现,其他实现是允许的。但是在C++中所有EDCOX1的1个函数都被设计成可以用VTABLE实现。

现在,在CFoovtable中有多少条目?有doSomething的条目吗?还有doSomething?还有doSomething?这样的模板允许生成无限的函数集。通常这没问题,因为您只使用有限的子集,但是对于虚拟函数,这个子集是未知的,因此vtable需要是无限的。

现在,您可能只需要vtable中的一个条目。在这种情况下,您可以这样写:

1
2
3
4
5
6
7
8
9
10
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;
};

这意味着用于CFoo的vtable将有一个条目,用于doSomething(float const&)的vtable。


您可以使用我们在Symbian中所称的"模板设计模式"。下面是示例代码,您可以了解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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;
};


如果您真的需要使这个方法成为虚拟的,可以考虑使CBar<>多态,并传递一个不模板化的基类型。

编辑:如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 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 ){
..
}


嗯,错误信息非常清楚。成员函数模板不能是虚拟的。如何解决这个问题取决于您的问题,但最简单的方法是使成员函数非虚拟化,并重新考虑您的设计。