模板可能不是“虚拟”

问题描述 投票:0回答:5

鉴于下面的代码,编译器显示一条消息指出

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'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 ){
    ...
}
c++ templates polymorphism virtual
5个回答
21
投票

了解为什么这是非法的最简单的原因是考虑 vtable。当然,这只是一种常见的实现,其他的实现也是允许的。但是 C++ 中的所有

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

这意味着

CFoo<int, float>
的 vtable 将有一个条目,即
doSomething(float const&)


5
投票

如果您确实需要使此方法成为虚拟方法,请考虑使

CBar<>
多态并传递未模板化的基本类型。

编辑:类似这样的:

// 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 ){
..
}

3
投票

您可以使用我们在 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;
}; 

0
投票

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


0
投票

因为草案作者太愚蠢了,以至于无法为每个可能的模板实例化添加一个条目来填充 vtable。

“C++ 中禁止使用虚拟模板函数 由于需要创建虚拟表的复杂性。 成员函数的模板不能声明为虚拟的。虚拟函数调用方法的标准实现采用固定大小的数据库,其中每个虚拟函数都有一个条目,这施加了这一限制。” -- https://guidingcode.com/virtual-function-template-class-cpp/

尽管您可以使用标头作为模板类的原型,但前提是标头实例化了适合模板的每种可能类型,并将类定义放入 .c++ 中

几年前,将 C++ 标准模板库移植到 C(通过宏进行多重继承)并做了更复杂的 vtable。

© www.soinside.com 2019 - 2024. All rights reserved.