C++ 使用模板类型为该模板类型生成唯一的成员名称

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

这个用例源于想要实现一个编译时事件总线数据结构,它只监听/注册/取消注册特定于提供的模板参数

从一个简单的实现开始,让我们假设我们有以下类AListener、AEvent、BListener和BEvent

我希望我的EventBus类如下所示:

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假设了一些相似的东西,甚至假设事件类型是不同的。在成员名称解析过程中,在重载选择之前,会出现歧义。为了解决这个问题,您必须为所有基类中的所有成员使用声明,这将使整个问题变得毫无意义。