如何使用多个参数专门化一个函数模板?

时间:2015-12-21 17:01:36

标签: c++ c++11 template-specialization enable-if

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

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

为了实现这一目标,我应该在代码中更改哪些内容?

2 个答案:

答案 0 :(得分:1)

Yout无法部分专门化功能模板。在您的情况下,您可以只使用两个模板参数定义第二个重载模板。

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

答案 1 :(得分:0)

由于这里给出的答案,尤其是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 );
}

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