【问题标题】:templates may not be ‘virtual’模板可能不是“虚拟的”
【发布时间】:2011-06-25 02:49:04
【问题描述】:

鉴于下面的代码,编译器会显示一条指向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 ){
    ...
}

【问题讨论】:

  • 这不是一个错误,它是一个特性,你不能声明一个虚拟的函数模板。你需要探索另一种方法,这取决于你想要做什么......
  • 问题是函数的参数是一个模板对象,我不能改变它的声明。
  • 为什么每个 CBar 实例都需要不同的函数?

标签: c++ templates polymorphism virtual


【解决方案1】:

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

现在,CFoo&lt;int&gt;vtable 中有多少条目? doSomething&lt;float&gt; 有条目吗?还有doSomething&lt;float*&gt;?还有doSomething&lt;float**&gt;?诸如此类的模板允许生成无限的函数集。通常这没问题,因为您只使用有限子集,但对于虚函数,这个子集是未知的,因此 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&lt;int, float&gt; 的vtable 将有一个条目,即doSomething(float const&amp;)

【讨论】:

  • 所有编译单元编译完成后,在链接阶段就可以知道。标准在这里丢弃有点快。
  • @v.oddou:不现实。链接器必须将所有虚拟调用与所有可能的基类匹配,并实例化模板。然后需要编译这些实例。这些新的实例又可能包含新的虚拟调用,因此这个过程必须是迭代的。
【解决方案2】:

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

【讨论】:

  • 感谢代码说明。我将doSomething 方法定义为template,因为它是参数。只有两种模板类型,一种对应CFoo,另一种由于CBar。我认为通过将CFoo&lt;FOO_TYPE, BAR_TYPE&gt; 声明为双模板类应该没问题。你怎么看?
  • 是的。是的,这应该消除错误并帮助您保留该功能的动态绑定/虚拟性。如果您想了解更多详细信息,请在此处查看此线程(Rainbow 是我 :-):groups.google.com/group/comp.lang.c++/browse_thread/thread/…
  • 再次格式化,但这并不意味着我喜欢这个答案。 “内联”注释表明对 C++ 缺乏熟悉。默认情况下,课堂定义为inline。而且您不能只将模板成员定义放在 .cpp 文件中。这必须在实例化时可见。
  • @MSalters 内联和虚拟评论是在 2011 年发表的。我无法从 c++ 标准中提取参考,但这里是该评论的快速链接。缺乏了解实际上是缺乏对语言的熟悉。 -> books.google.com/…
【解决方案3】:

如果您确实需要将此方法设为虚拟,请考虑将CBar&lt;&gt; 设为多态并传递未模板化的基类型。

编辑:像这样:

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

    嗯,错误信息很清楚。 Member function templates can't be virtual。如何解决这个问题取决于您的问题,但最简单的做法是将成员函数设为非虚拟并重新考虑您的设计。

    【讨论】:

    • 谢谢。就我而言,我需要将此“refBar”作为参数,并且它属于模板类。
    • 你知道会有多少种不同的模板参数吗? 3? 8?你可以重载每个函数吗?如果不知道,编译器怎么知道有多少个虚函数?
    • @Bo:只有两个参数:FOO_TYPE 和 BAR_TYPE
    • 我在想 BAR_TYPE 可能的值的数量。如果你知道它们是什么,你就可以拥有那么多没有模板化的虚函数。
    猜你喜欢
    • 1970-01-01
    • 2018-06-18
    • 2023-04-10
    • 2015-01-04
    • 2011-01-22
    • 2012-10-08
    相关资源
    最近更新 更多