鉴于下面的代码,编译器显示一条消息指出
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 ){
...
}
了解为什么这是非法的最简单的原因是考虑 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&)
。
如果您确实需要使此方法成为虚拟方法,请考虑使
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 ){
..
}
您可以使用我们在 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;
};
嗯,错误消息非常清楚。 成员函数模板不能是虚拟的。如何解决这个问题取决于您的问题,但最简单的方法是使成员函数成为非虚拟函数并重新考虑您的设计。
因为草案作者太愚蠢了,以至于无法为每个可能的模板实例化添加一个条目来填充 vtable。
“C++ 中禁止使用虚拟模板函数 由于需要创建虚拟表的复杂性。 成员函数的模板不能声明为虚拟的。虚拟函数调用方法的标准实现采用固定大小的数据库,其中每个虚拟函数都有一个条目,这施加了这一限制。” -- https://guidingcode.com/virtual-function-template-class-cpp/
尽管您可以使用标头作为模板类的原型,但前提是标头实例化了适合模板的每种可能类型,并将类定义放入 .c++ 中
几年前,将 C++ 标准模板库移植到 C(通过宏进行多重继承)并做了更复杂的 vtable。