如何从内部别名推断模板参数?

  • 本文关键字:参数 内部 别名 c++ templates
  • 更新时间 :
  • 英文 :


我有一个模板类,有两个参数:

template<class TEvent, class TData>
class EventPool {
public:
using EventObserver = std::function<void(const TData &)>;
using EventData = TData;
using EventType = TEvent;
public:
EventObserverId observe(TEvent event, EventObserver &&observer) { ... }
void deleteObserver(EventObserverId observerId) { ... }
void dispatch(TEvent event, const TData &data) { ... }
};

我还有另一个容器类来存储许多池:

class EventSystem {
using CollisionPool = EventPool<CollisionEvent, CollisionData>;
using MouseButtonPool = EventPool<MouseButtonEvent, MouseButtonData>;
using KeyboardPool = EventPool<KeyboardButtonEvent, KeyboardButtonData>;
public:

private:
std::tuple<CollisionPool, MouseButtonPool, KeyboardPool> mPools;
};

现在,我想创建一个基于模板的函数,它自动推断正确的池并调用池内的函数。例如:

template<class TEvent>
EventObserverId observe(TEvent event, DeriveThePoolHere::EventObserver &&observer) {
auto &pool = getPoolByFromEventType<TEvent>();
return pool.observe(event, observer);
}
template<class TEvent>
void deleteObserver(TEvent event, EventObserverId observerId) {
auto &pool = getPoolFromEevntType<TEvent>();
pool.deleteObserver(observerId);
}
void dispatch(TEvent event, const DeriveThePoolHere::EventData &data) {
auto &pool = getPoolFromEventType<TEvent>();
pool.dispatch(event, data);
}

如果我能以某种方式创建一个类型映射映射事件到事件数据,我可以解决这个问题,但我不确定如何在c++中做到这一点。

if constexprstd::apply的帮助下(需要c++17)
可能像这样:

template<typename TPool, typename TEvent, typename TData>
void dispatchIfMatch(TPool& pool, TEvent event, const TData& data) {
if constexpr(std::is_same<TEvent, typename TPool::EventType>::value) {
pool.dispatch(event, data);
}
}
template<typename TEvent, typename TData>
void dispatch(EventSystem& esys, TEvent event, const TData& data) {
std::apply(
[&](auto&&... args) {
((dispatchIfMatch(args, event, data)), ...);
}, esys.mPools);
}

在这里测试:http://coliru.stacked-crooked.com/a/2c2231c860d8023c

我能够使用SFINAE解决这个问题(它被称为什么?)。我创建了一个空结构体,并用不同的事件对其进行专门化:

namespace detail {
template<class TEvent>
struct GetEventData {};
template<>
struct GetEventData<CollisionEvent> {
using Data = CollisionData;
};
template<>
struct GetEventData<MouseButtonEvent> {
using Data = MouseButtonData;
};
}

为了使我的工作更容易,我在类中创建了另一个别名(注意,这种类型的专门化不能在类中):

class EventSystem {
template<class TEvent>
using GetEventPool = EventPool<TEvent, detail::GetEventData<TEvent>;
public:
// explained later
private:
std::tuple<GetEventPool<CollisionEvent>, GetEventPool<MouseButtonEvent>> mPools;
};

由于GetEventPool是一个类型别名,我在EventPool中有类型别名,并且元组项可以按类型检索,我能够将这三件事组合在一起:

template<class TEvent>
EventObserverId observe(TEvent event, typename GetEventPool<TEvent>::EventObserver &&observer) {
using CurrentPool = GetEventPool<TEvent>;
return std::get<CurrentPool>(mPools).observe(event, observer);
}
template<class TEvent>
void deleteObserver(TEvent event, typename EventObserverId observerId) {
using CurrentPool = GetEventPool<TEvent>;
std::get<CurrentPool>(mPools).deleteObserver(observerId);
}
template<class TEvent>
void dispatch(TEvent event, const typename GetEventPool<TEvent>::EventData &data) {
using CurrentPool = GetEventPool<TEvent>;
std::get<CurrentPool>(mPools).dispatch(event, data);
}

这工作得很好,如果传递不正确的事件或事件类型,也会显示编译器错误。在数据类型方面会有一些冗余;因此,我仍在尝试做的一件事是简化元组:

PoolTuple<CollisionEvent, MouseButtonEvent, ...> mPools;

编辑:为了简化元组声明,我找到了这个有趣的语法:

template<class... TEvents>
using PoolTuple = std::tuple<GetEventPool<TEvent>...>;
PoolTuple<CollisionEvent, MouseButtonEvent, ...> mPools;

最新更新