我已经创建了基于回调的简单事件管理器并且它可以工作,但我有一些错误,模板参数为零。
class event_manager
{
public:
template <typename... T>
static void register_event(const unsigned long e, std::function<void(T...)> ec)
{
events.insert({ e, ec });
}
template <typename... T>
static void fire_event(const unsigned long e, T... args)
{
for (auto it : events)
{
if (it.first == e)
{
boost::any_cast<std::function<void(T...)>>(it.second)(args...);
}
}
}
private:
static std::unordered_multimap<unsigned int, boost::any> events;
};
我正在使用此代码添加回调:
event_manager::register_event<unsigned int>(DVU_EVENT_KEY_PRESSED, [](unsigned int key)
{
//Works!
});
event_manager::register_event(DVU_EVENT_IDLE, []()
{
//Could not deduce template argument
});
第二个问题:
是否可以更改代码以删除<unsigned int>
- 如模板规范?
示例:
event_manager::register_event(DVU_EVENT_KEY_PRESSED, [](unsigned int key){}));
答案 0 :(得分:1)
由于lambda只是一个带有operator()
的仿函数,你可能只是有一个重载,最终会推断它:
template <typename F>
static void register_event(const unsigned long e, F f)
{
register_event(e, f, &F::operator());
}
template <typename F, typename R, typename... T>
static void register_event(const unsigned long e, F& f,
R (F::*method)(T...) const)
{
std::function<R(T...)> func = f;
events.insert({ e, func });
}
可能需要R == void
或static_assert
或其他。
答案 1 :(得分:1)
即使是第一个也没有像你一样编译here。
std::function
与lambda不完全匹配,并且当您使用可变参数模板时,您不能以这种方式指定所有类型(因为您指定了第一种类型,编译器可能会推断出其余类型)。 / p>
可能的解决方法是仅传递func
template <typename Func>
static void register_event(const unsigned long e, Func ec);
并使用Func::operator()
答案 2 :(得分:0)
您的设计是不安全的,因为依靠类型推导来生成完全匹配的类型是脆弱的,并且您的演员需要完全匹配的类型。
这是一个略有不同的设计:
class event_manager {
public:
template <typename Signature>
static void register_event(const unsigned long e, std::function<Signature> ec) {
events.emplace( e, std::move(ec) );
}
template <typename Signature, typename...Ts>
static void fire_event(const unsigned long e, Ts...&& args) {
auto r = events.equal_range( e );
for (auto it = r.first; it != r.second; ++it)
{
auto&& f = boost::any_cast<std::function<Signature> const&>(it.second);
f(std::forward<Ts>(args)...);
}
}
private:
static std::unordered_multimap<unsigned int, boost::any> events;
};
这里传递两端的函数签名,如void()
或void(int)
。这些类型必须完全匹配。
我完全将fire_event
的参数转发给我从地图中拉出来的函数。
我做了一些其他改进,比如正确移动/放置并删除了一些虚假副本。
出于某些原因,推断lambda的签名是一个坏主意。首先,因为C ++ 14 auto
lambdas即将到来。其次,它意味着lambda或函数需要T const&
或T
或其他任何&#34;泄漏&#34;如何调用它(你的原始实现要求所有值都按值进行)。
现在,您在注册它的位置以及触发它的位置都会明确列出给定事件的签名。如果它不匹配,应该更容易注意到。
我也会被any_cast
指向/从指针移动,并声明它是非空的,而不是在你得到签名错误时抛出。