如何专门化具有多个参数的函数模板?
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 );
,甚至使用基础 class 作为参数,这就是为什么我在模板中有一个单独的事件类型 E 和参数类型 P:
bool MyNode::mouseDown( const Event &event );
我也需要支持:
connectEvent<DrawEvent>( &MyNode::draw, this );
,其中回调函数定义为:
bool MyNode::draw();
问题:为了支持后者,我想专门针对参数 P 为空的情况的 connectEvent
函数,因为它需要对 std::bind
。我已经尝试了很多不同的方法,包括在基本模板上使用 enable_if
和 is_void
的组合,但是 none 编译了,所以我一定是做错了什么并且已经求助于试用 -并且-此时出错。
在大多数情况下,Visual Studio 2015 编译器会抱怨 "illegal use of explicit template arguments"。
这是我认为可行但行不通的代码版本:
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 ) { ...}
Specialising function template is usually not a good idea anyway. 解决方法是定义一个模板函数并在内部分派到一个可以部分特化的模板 class:
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);
}
感谢这里给出的答案,尤其是T.C.的评论,我意识到问题出在两个函数的组合上:即使我们可以编写两个有效版本,编译器也不会能够正确区分它们,因为它将 void
视为参数并始终尝试构建第一个版本。然后它报告尝试使用 const void ¶m
.
的错误
唯一可行的解决方案是为 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 );
}
但这是一个不同的问题。无论如何,感谢大家的投入。
考虑以下模板化成员函数:
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 );
,甚至使用基础 class 作为参数,这就是为什么我在模板中有一个单独的事件类型 E 和参数类型 P:
bool MyNode::mouseDown( const Event &event );
我也需要支持:
connectEvent<DrawEvent>( &MyNode::draw, this );
,其中回调函数定义为:
bool MyNode::draw();
问题:为了支持后者,我想专门针对参数 P 为空的情况的 connectEvent
函数,因为它需要对 std::bind
。我已经尝试了很多不同的方法,包括在基本模板上使用 enable_if
和 is_void
的组合,但是 none 编译了,所以我一定是做错了什么并且已经求助于试用 -并且-此时出错。
在大多数情况下,Visual Studio 2015 编译器会抱怨 "illegal use of explicit template arguments"。
这是我认为可行但行不通的代码版本:
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 ) { ...}
Specialising function template is usually not a good idea anyway. 解决方法是定义一个模板函数并在内部分派到一个可以部分特化的模板 class:
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);
}
感谢这里给出的答案,尤其是T.C.的评论,我意识到问题出在两个函数的组合上:即使我们可以编写两个有效版本,编译器也不会能够正确区分它们,因为它将 void
视为参数并始终尝试构建第一个版本。然后它报告尝试使用 const void ¶m
.
唯一可行的解决方案是为 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 );
}
但这是一个不同的问题。无论如何,感谢大家的投入。