使用模板化成员函数指针作为模板参数

时间:2017-03-20 09:29:22

标签: c++ templates c++14 type-alias

我有这堂课:

#include <assert.h>

template <typename DeclaringType, typename HandlerType>
using SubscribeMethodType = void (DeclaringType::*)(HandlerType* handler);

template <typename DeclaringType>
using UnsubscribeMethodType = void (DeclaringType::*)();

template <SubscribeMethodType<???_1, ???_2> subscribeMethod, UnsubscribeMethodType<???_1> unsubscribeMethod>
class EventHandler
{
private:
    template <typename T>
    struct ExtractDeclaringAndHandlerTypes
    {
    };

    template <typename DeclaringType, typename HandlerType>
    struct ExtractDeclaringAndHandlerTypes<void (DeclaringType::*)(HandlerType*)>
    {
        typedef DeclaringType DeclaringType;
        typedef HandlerType HandlerType;
    };

    typedef typename ExtractDeclaringAndHandlerTypes<decltype(subscribeMethod)>::DeclaringType DeclaringType;
    typedef typename ExtractDeclaringAndHandlerTypes<decltype(subscribeMethod)>::HandlerType HandlerType;

public:
    EventHandler() { }
    ~EventHandler()
    {
        Unsubscribe();
    }

    void Subscribe(DeclaringType* eventOwner, HandlerType* handler)
    {
        assert(!m_IsSubscribed);

        m_EventOwner = eventOwner;
        (eventOwner->*subscribeMethod)(handler);
        m_IsSubscribed = true;
    }

    void Unsubscribe()
    {
        if (m_IsSubscribed)
        {
            (m_EventOwner->*unsubscribeMethod)();
            m_IsSubscribed = false;
        }
    }

private:
    DeclaringType* m_EventOwner;
    bool m_IsSubscribed;
};

使用示例:

class IEventConsumer
{
};

class ExampleEventOwner
{
public:
    void Subscribe(IEventConsumer* consumer) {}
    void Unsubscribe() {}
};

int main()
{
    ExampleEventOwner owner;
    IEventConsumer consumer;

    EventHandler<&ExampleEventOwner::Subscribe, &ExampleEventOwner::Unsubscribe> handler;
    handler.Subscribe(&owner, &consumer);
    handler.Unsubscribe();

    return 0;
}

是否可以指定某些内容而不是“??? _ 1”和“??? _ 2”以允许它们为任何类型?目标是避免此类的使用者必须明确指定DeclaringType和HandlerType,因为可以从subscribeMethod和unsubscribeMethod轻松推断出它们。

注意:我不能使用C ++ 17。

1 个答案:

答案 0 :(得分:1)

  

是否可以指定一些东西而不是&#34; ??? _ 1&#34;和&#34; ??? _ 2&#34;允许他们成为任何类型?

不是在C ++ 17之前,没有。你要求的是一个模板非类型参数,它可以从提供的值中推断出它的类型 - 这正是新的C ++ 17语言特性template auto所做的。

在C ++ 17之前,做这种事情的唯一方法是首先提供类型:

template <class T, T Value> struct X;

所以你必须写:

EventHandler<decltype(&ExampleEventOwner::Subscribe), &ExampleEventOwner::Subscribe,
    decltype(&ExampleEventOwner::Unsubscribe), &ExampleEventOwner::Unsubscribe
> handler;
不可否认,这很糟糕。或者,您可以更改设计以使模板参数成为类类型和处理程序类型。这将允许你写:

EventHandler<ExampleEventOwner, IEventConsumer> handler(
    &ExampleEventOwner::Subscribe, &ExampleEventOwner::Unsubscribe);

反过来会让你写一个工厂函数:

template <class T, class H>
EventHandler<void (T::*)(H*), void(T::*)()> makeEventHandler(
    void (T::*subscribe)(H*), void (T::*unsubscribe)());

auto handler = makeEventHandler(&ExampleEventOwner::Subscribe,
    &ExampleEventOwner::Unsubscribe);

这绝对不可怕。