代码之家  ›  专栏  ›  技术社区  ›  Paul Houx

如何专门化具有多个参数的函数模板?

  •  0
  • Paul Houx  · 技术社区  · 10 年前

    考虑以下模板化成员函数:

    template<typename E, typename N, typename P>
    void Node::connectEvent( const bool( N::*fn )( const P& ), N *inst )
    {
        // Obtain unique event ID based on type.
        size_t eventId = typeid( E ).hash_code();
    
        // Actual code wraps the function returned from std::bind, 
        // but for this example let's assume we can store it directly. 
        mCallbacks[eventId] = std::bind( fn, inst, std::placeholders::_1 );
    }
    

    我希望能够以以下方式调用此函数:

    connectEvent<MouseDownEvent>( &MyNode::mouseDown, this );
    

    ,其中回调函数定义为:

    bool MyNode::mouseDown( const MouseDownEvent &event );
    

    ,甚至使用基类作为参数,这就是为什么我在模板中有单独的事件类型E和参数类型P:

    bool MyNode::mouseDown( const Event &event );
    

    我还需要支持:

    connectEvent<DrawEvent>( &MyNode::draw, this );
    

    ,其中回调函数定义为:

    bool MyNode::draw();
    

    问题 :为了支持后者,我想专门研究 connectEvent 函数,因为它需要对 std::bind 。我尝试了许多不同的方法,包括使用 enable_if is_void 在基本模板上,但没有编译过,所以我一定做错了什么,在这一点上我已经尝试过了。

    在大多数情况下,VisualStudio2015编译器抱怨“非法使用显式模板参数”。

    这是一个我认为可以工作但没有成功的代码版本:

    template<typename E, typename N>
    void Node::connectEvent<E,N,void>( const bool( N::*fn )(void), N *inst )
    {
        size_t eventId = typeid( E ).hash_code();
        mCallbacks[eventId] = std::bind( fn, inst );
    }
    

    为了实现这一点,我应该在代码中更改什么?

    2 回复  |  直到 10 年前
        1
  •  2
  •   Jens    10 年前

    您不能部分专门化函数模板。在您的情况下,您只需要定义一个只有两个模板参数的第二个重载模板。

    template<typename E, typename N, typename P>
    void Node::connectEvent( const bool( N::*fn )( const P& ), N *inst ) { ...}
    
    template<typename E, typename N>
    void Node::connectEvent( const bool( N::*fn )(), N *inst ) { ...}
    

    Specialising function template is usually not a good idea anyway. 解决方法是定义一个模板函数,并在内部分派给一个可以部分专用化的模板类:

    template<typename E, typename N, typename P> struct C
    {
        static void connectEvent(const bool( N::*fn )( const P& ), N *inst)
        {
            size_t eventId = typeid( E ).hash_code();
            mCallbacks[eventId] = std::bind( fn, inst );
        }
    
    };
    
     template<typename E, typename N> struct C<E,N,void> {
        ... specialize ...
     };
    
    template<typename E, typename N, typename P>
    void Node::connectEvent( const bool( N::*fn )( const P& ), N *inst ) 
    {
        C<E,N,P>::connectEvent(fn, inst);
    }
    
    template<typename E, typename N>
    void Node::connectEvent( const bool( N::*fn )(), N *inst ) 
    {
        C<E,N,void>::connectEvent(fn, inst);
    }
    
        2
  •  0
  •   Paul Houx    10 年前

    感谢这里给出的答案,特别是T.C.的评论,我意识到问题在于这两个函数的组合:即使我们可以编写两个有效版本,编译器也无法正确区分它们,因为它处理 void 作为参数,并始终尝试构造第一个版本。然后报告尝试使用 const void &param .

    唯一有效的解决方案是为 connectEvent 功能,例如:

    template<typename E, typename N>
    void Node::connectVoidEvent( const bool( N::*fn )( ), N *inst );
    

    ,这样编译器就不会混淆我要使用的版本。不过,我可能不会使用它,而是始终需要回调方法的单个参数。

    值得一提的是:我也试着这样做,把问题转移到 std::bind ,但不知道从哪里开始:

    template<typename E, typename F, typename N>
    void Node::connectEvent( const F &fn, N *inst )
    {
        // F has type:          (__thiscall MyNode::*)(void)
        // or something like:   (__thiscall MyNode::*)(const Event&)
    
        size_t eventId = typeid( E ).hash_code();
    
        // How will the compiler know the number of parameters of F,
        // and whether or not to use placeholders?
        mCallbacks[eventId] = std::bind( fn, inst, std::placeholders::_1 );
    }
    

    但这是一个不同的问题。无论如何,谢谢大家的意见。