C++ 使用模板类型为该模板类型生成唯一的成员名称
这个用例源于想要实现一个编译时事件总线数据结构,它只监听/注册/取消注册特定于提供的模板参数 从一个简单的实现开始,让我们假设我们有以下类AListener、AEvent、BListener和BEvent 我希望我的EventBus类如下所示:C++ 使用模板类型为该模板类型生成唯一的成员名称,c++,templates,c++17,C++,Templates,C++17,这个用例源于想要实现一个编译时事件总线数据结构,它只监听/注册/取消注册特定于提供的模板参数 从一个简单的实现开始,让我们假设我们有以下类AListener、AEvent、BListener和BEvent 我希望我的EventBus类如下所示: class EventBus { std::vector<AListener*> aListeners; std::vector<BListener*> bListeners; public: void
class EventBus {
std::vector<AListener*> aListeners;
std::vector<BListener*> bListeners;
public:
void registerListener(AListener& listener);
void unregisterListener(AListener& listener);
void sendEvent(AEvent event);
void registerListener(BListener& listener);
void unregisterListener(BListener& listener);
void sendEvent(BEvent event);
};
当然,除非有更好的办法?这可能吗?您可以使用元组和一些类型特征:
#include <iostream>
#include <vector>
#include <tuple>
#include <utility>
template<typename x_Event> class
t_EventTrait;
template<typename ... x_Listener> class
t_EventBus
{
private: ::std::tuple<::std::vector<x_Listener *>...> m_listeners;
public: template<typename xx_Listener> void
Register_Listener(xx_Listener & listener)
{
::std::get<::std::vector<xx_Listener *>>(m_listeners).emplace_back(&listener);
}
public: template<typename x_Event> void
Send_Event(x_Event & event)
{
for(auto p_listener: ::std::get<::std::vector<typename t_EventTrait<x_Event>::t_Listener *>>(m_listeners))
{
p_listener->On_Event(event);
}
}
};
struct t_EventA {};
struct t_ListenerA { void On_Event(t_EventA &) { ::std::cout << "handling A\n"; } };
template<> class t_EventTrait<t_EventA>{ public: using t_Listener = t_ListenerA; };
struct t_EventB {};
struct t_ListenerB { void On_Event(t_EventB &) { ::std::cout << "handling B\n"; } };
template<> class t_EventTrait<t_EventB>{ public: using t_Listener = t_ListenerB; };
int main()
{
t_EventBus<t_ListenerA, t_ListenerB> bus{};
t_ListenerA a{};
bus.Register_Listener(a);
t_EventA ea{};
bus.Send_Event(ea);
t_ListenerB b{};
bus.Register_Listener(b);
t_EventB eb{};
bus.Send_Event(eb);
return 0;
}
您可以使用元组和一些类型特征:
#include <iostream>
#include <vector>
#include <tuple>
#include <utility>
template<typename x_Event> class
t_EventTrait;
template<typename ... x_Listener> class
t_EventBus
{
private: ::std::tuple<::std::vector<x_Listener *>...> m_listeners;
public: template<typename xx_Listener> void
Register_Listener(xx_Listener & listener)
{
::std::get<::std::vector<xx_Listener *>>(m_listeners).emplace_back(&listener);
}
public: template<typename x_Event> void
Send_Event(x_Event & event)
{
for(auto p_listener: ::std::get<::std::vector<typename t_EventTrait<x_Event>::t_Listener *>>(m_listeners))
{
p_listener->On_Event(event);
}
}
};
struct t_EventA {};
struct t_ListenerA { void On_Event(t_EventA &) { ::std::cout << "handling A\n"; } };
template<> class t_EventTrait<t_EventA>{ public: using t_Listener = t_ListenerA; };
struct t_EventB {};
struct t_ListenerB { void On_Event(t_EventB &) { ::std::cout << "handling B\n"; } };
template<> class t_EventTrait<t_EventB>{ public: using t_Listener = t_ListenerB; };
int main()
{
t_EventBus<t_ListenerA, t_ListenerB> bus{};
t_ListenerA a{};
bus.Register_Listener(a);
t_EventA ea{};
bus.Send_Event(ea);
t_ListenerB b{};
bus.Register_Listener(b);
t_EventB eb{};
bus.Send_Event(eb);
return 0;
}
在C++11中,可以使用可变模板
template<class... MoreEventPairs> class EventBus {};
template<class Listener, class Event>
class EventBus<Listener, Event>
{
private:
std::vector<Listener *> Listeners;
public:
EventBus() {};
~EventBus() {};
void registerListener(Listener& listener) {}; // dummy implementations here
void unregisterListener(Listener& listener) {};
void sendEvent(Event event) {};
};
template<class Listener, class Event, class ... MoreEventPairs>
class EventBus<Listener, Event, MoreEventPairs ...> : public EventBus<Listener, Event>,
public EventBus<MoreEventPairs ...>
{
public:
// these are needed so name resolution works
// one needed for each function, on both inheritance paths
using EventBus<Listener, Event>::registerListener;
using EventBus<Listener, Event>::unregisterListener;
using EventBus<Listener, Event>::sendEvent;
using EventBus<MoreEventPairs ...>::registerListener;
using EventBus<MoreEventPairs ...>::unregisterListener;
using EventBus<MoreEventPairs ...>::sendEvent;
};
// construct as
EventBus<ListenerA, EventA, ListenerB, EventB> bus;
registerListener、unregisterListener和sendEvent成员函数都是非虚拟函数,因为您不希望它们被EventBus覆盖,而EventBus随后会受到隐藏规则的影响
除了假设任何侦听器或事件类之间没有继承关系外,上述两种方法都假设侦听器和事件类都是不同的类型,即没有多次列出的侦听器类或事件类。如果打破这一假设,最有可能的结果是某些成员函数的调用将变得不明确。在C++11中,可以使用可变模板
template<class... MoreEventPairs> class EventBus {};
template<class Listener, class Event>
class EventBus<Listener, Event>
{
private:
std::vector<Listener *> Listeners;
public:
EventBus() {};
~EventBus() {};
void registerListener(Listener& listener) {}; // dummy implementations here
void unregisterListener(Listener& listener) {};
void sendEvent(Event event) {};
};
template<class Listener, class Event, class ... MoreEventPairs>
class EventBus<Listener, Event, MoreEventPairs ...> : public EventBus<Listener, Event>,
public EventBus<MoreEventPairs ...>
{
public:
// these are needed so name resolution works
// one needed for each function, on both inheritance paths
using EventBus<Listener, Event>::registerListener;
using EventBus<Listener, Event>::unregisterListener;
using EventBus<Listener, Event>::sendEvent;
using EventBus<MoreEventPairs ...>::registerListener;
using EventBus<MoreEventPairs ...>::unregisterListener;
using EventBus<MoreEventPairs ...>::sendEvent;
};
// construct as
EventBus<ListenerA, EventA, ListenerB, EventB> bus;
registerListener、unregisterListener和sendEvent成员函数都是非虚拟函数,因为您不希望它们被EventBus覆盖,而EventBus随后会受到隐藏规则的影响
除了假设任何侦听器或事件类之间没有继承关系外,上述两种方法都假设侦听器和事件类都是不同的类型,即没有多次列出的侦听器类或事件类。如果打破这一假设,最有可能的结果是某些成员函数的调用将变得不明确。我将创建一个类来处理侦听器/事件:
template <typename Listener, Event>
class EventHandler {
std::vector<Listener*> mListeners;
public:
void registerListener(Listener& listener);
void unregisterListener(Listener& listener);
void sendEvent(Event event);
};
然后,处理所有问题的类将是:
template <typename ... Ts>
class EventBus : Ts...
{
public:
using Ts::registerListener...; // Requires C++17
using Ts::unregisterListener...; // Prior that you have to do it with recursion
using Ts::sendEvent...; // class EventBus<T, Rest...> : T, EventBus<Rest...>
};
使用方法:
EventBus<EventHandler<AListener, AEvent>, EventHandler<BListener, BEvent>> eventBus;
顺便说一句,事件可能依赖于侦听器,因此使用typename Listener::Event似乎合适,并删除所有事件模板参数。我将创建一个类来处理侦听器/事件:
template <typename Listener, Event>
class EventHandler {
std::vector<Listener*> mListeners;
public:
void registerListener(Listener& listener);
void unregisterListener(Listener& listener);
void sendEvent(Event event);
};
然后,处理所有问题的类将是:
template <typename ... Ts>
class EventBus : Ts...
{
public:
using Ts::registerListener...; // Requires C++17
using Ts::unregisterListener...; // Prior that you have to do it with recursion
using Ts::sendEvent...; // class EventBus<T, Rest...> : T, EventBus<Rest...>
};
使用方法:
EventBus<EventHandler<AListener, AEvent>, EventHandler<BListener, BEvent>> eventBus;
顺便说一句,事件可能依赖于侦听器,因此使用typename Listener::Event似乎合适,并删除所有事件模板参数。我知道它可能是私有的,但您希望从这种实现中获得什么?@miradham我不明白您所说的增益是什么意思,因此,如果我不得不猜测你的意思,我写的可能不正确,因为你的单词选择有歧义:非常高性能的代码,同时也允许我写尽可能少的代码来添加新的侦听器和类型。我知道这可能是隐私,但是你期望从这种实现中获得什么?@miradham我不明白你所说的增益是什么意思,所以如果我不得不猜测你的意思,我写的可能不正确,因为你的单词选择含糊不清:非常高效的代码,同时还允许我编写尽可能少的代码来添加新的侦听器和类型。这不起作用,因为从基类调用任何方法,如bus.sendEvent。。。将是不明确的。@VTT-不假设事件类型是不同的。OP假设了一些相似的东西,甚至假设事件类型是不同的。在成员名称解析过程中,在重载选择之前,会出现歧义。为了解决这个问题,您必须为所有基类中的所有成员使用声明,这将使整个事情变得毫无意义。这不会起作用,因为从基类调用任何方法,如bus.sendEvent。。。将是不明确的。@VTT-不假设事件类型是不同的。OP假设了一些相似的东西,甚至假设事件类型是不同的。在成员名称解析过程中,在重载选择之前,会出现歧义。为了解决这个问题,您必须为所有基类中的所有成员使用声明,这将使整个问题变得毫无意义。