繁体   English   中英

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

[英]How to specialize a function template with more than 1 parameter?

考虑以下模板成员函数:

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();

问题 :为了支持后者,我想专门针对参数P为空的情况的connectEvent函数,因为它需要对std::bind进行不同的调用。 我尝试了许多不同的方法,包括在基本模板上使用enable_ifis_void的组合,但是没有进行编译,因此我必须做错了一些,并且在这一点上采用了反复试验。

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

这是我认为可以使用的代码版本,但没有奏效:

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

为了使之成为可能,我应该在代码中进行哪些更改?

您不能部分专门化功能模板。 在您的情况下,您可以仅使用两个模板参数定义第二个过载模板。

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

无论如何,专业化功能模板通常不是一个好主意。 解决方法是定义一个模板函数并在内部分派给可以部分专门化的模板类:

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

多亏了这里给出的答案,尤其是TC的话,我才意识到问题出在两个函数的结合上:即使我们可以编写两个有效的版本,编译器也无法正确区分它们,因为它将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 );
}

但这是一个不同的问题。 无论如何,感谢大家的投入。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM