C++:使用 void 作为模板参数

C++: use void as template argument

我有这个最小的 class 来表示客户端可以订阅的事件。
该事件可以有一个与之关联的数据类型,因此当它被发布者触发时,该类型的参数将传递给客户端的回调:

template<typename Arg, typename Callback = function<void(const Arg&)>>
class Event
{
public:
    Event(Callback c) : mCallback(c){}
    
    void Trigger(const Arg& arg) {
        mCallback(arg);
    }

private:
    Callback mCallback;
};

现在我可以创建一个 Event<int> 或任何其他具体类型,但允许“空”事件对我来说真的很重要,它没有与之关联的数据:Event<void>

但遗憾的是,这不起作用:

static void FooVoid() {
    cout << "Look ma, no args!" << endl;
}

static void FooInt(int a) {
    cout << "int arg " << a << endl;
}

int main()
{
    /* Compiles */
    Event<int> eInt(&FooInt);
    eInt.Trigger(42);

    /* Does not compile :(
    Event<void> eVoid(&FooVoid);
    eVoid.Trigger();
    */
    return 0;
}

有什么方法可以实现这个愿望API?怎么样?

(P.S 该解决方案应该适用于 C++11)

解决此问题的最快方法是使用 parameter pack (added in C++11) for your template argument instead of a single type and using an empty parameter pack instead of void. A parameter pack can homogeneously hold any number of type, including 0 and 1. Then it can be used to generate the right types and member functions. You basically just have to add ... correctly near every use of Arg (link) :

#include <functional>
#include <iostream>

template<typename ... Arg>
class Event
{
public:
    using Callback = std::function<void(const Arg&...)>;

    Event(Callback c) : mCallback(c){}
    
    void Trigger(const Arg& ... arg) {
        mCallback(arg...);
    }

private:
    Callback mCallback;
};

static void FooVoid() {
    std::cout << "Look ma, no args!" << std::endl;
}

static void FooInt(int a) {
    std::cout << "int arg " << a << std::endl;
}

int main()
{
    /* Compiles */
    Event<int> eInt(&FooInt);
    eInt.Trigger(42);

    Event<> eVoid(&FooVoid);
    eVoid.Trigger();
    
    return 0;
}

这还有一个好处,就是您可以使用带有多个参数的回调。如果这不是您想要的,您可以添加一个 static_assert 来防止它:

template<typename ... Arg>
class Event
{
public:
    using Callback = std::function<void(const Arg&...)>;
    static_assert(sizeof...(Arg) <= 1, "Too many arguments");

    Event(Callback c) : mCallback(c){}
    
    void Trigger(const Arg& ... arg) {
        mCallback(arg...);
    }

private:
    Callback mCallback;
};

请注意,此解决方案需要 Event<> 而不是 Event<void>。您可以通过为 Event<void> 添加一个使用 Event<> (link) :

的简短专业化来解决这个问题
template<>
class Event<void> : public Event<>
{
    // Inherit constructors
    using Event<>::Event;
};