我试图了解事件处理/回调中的c ++ 0x std :: bind和std :: function用法。所以我正在研究一些代码片段并遇到一些我无法完全理解的有趣的地方。
假设我们有:
class EventHandler
{
public:
template<typename Event, typename Listener>
bool bindEvent (const Listener& function)
{
if (std::is_array<Event>::value)
{
std::cout << "You cannot register an array as an event type" << std::endl;
return false;
}
if (std::is_convertible<Listener, std::function<void (Event&)>>::value)
{
std::cout << "Invalid callback for this event type" << std::endl;
return false;
}
listeners.insert(std::make_pair(&typeid(typename std::decay<Event>::type),
[=](void* ev) { function(*static_cast<Event*>(ev)); }));
}
private:
// for each event type, we have a list of callbacks
// these callbacks are of the form std::function<void (void*)>
// because we will convert between the &event and void*
typedef std::function <void (void*)> fun;
std::unordered_multimap <const std::type_info*, fun> listeners;
};
// example of an event type
struct KeyboardEvent
{
int keyCode;
};
// example of an event callback
void do_key (const KeyboardEvent &event)
{
std::cout << "I am working" << std::endl;
}
int main (int argc, char** argv)
{
EventHandler handler;
handler.bindEvent<KeyboardEvent> (&do_key); // fixed typo
return 0;
}
问题:监听器在此部分中包含哪种类型?:
template<typename Event, typename Listener>
bool bindEvent(const Listener& function)
因为在main方法中我们只使用。
调用此函数 PS:此外,此代码在std :: is_convertible部分失败。 (据我了解,因为来自habindEvent<KeyboardEvent> (&do_key);
答案 0 :(得分:6)
Listener
将被编译器推断为您传递它的函数指针的类型,在本例中为void(const KeyboardEvent&)
。
你的测试失败了,因为这是错误的方法:你想要
if (!std::is_convertible<Listener, std::function<void (Event&)>>::value)
而是(注意否定)。
顺便说一句,std::is_array
和std::is_convertable
都是在编译时决定的,这意味着您正在使用运行时检查来查找静态确定的内容。相反,您可以使用SFINAE使模板无法绑定无效类型:
template<typename Event, typename Listener>
typename std::enable_if<!std::is_array<Event>::value && std::is_convertible<Listener, std::function<void(Event&)>>::value, bool>::type bindEvent (const Listener& function)
{
}
如果您尝试使用与您的条件不匹配的类型实例化模板,则会导致编译器错误。
答案 1 :(得分:1)
首先,我假设habindEvent<KeyboardEvent> (&do_key);
是一个拼写错误,应该是handler.bindEvent<KeyboardEvent>(&do_key)
。
因此,侦听器的类型是从参数中扣除的模板。所以在你的特殊情况下就是这样。
typedef void(*Fn_t)(const KeyboardEvent &);
EventHandler handler;
handler.bindEvent<KeyboardEvent, Fn_t> (&do_key);
但是你不需要Fn_t,因为编译器可以为你做这项工作 修好你的类型之后,代码会在我的机器上编译。