微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决C++:使用 void 作为模板参数

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

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)

解决方法

无需明确专门针对 void 解决此问题的最快方法是使用 parameter pack(在 C++11 中添加)作为模板参数而不是单一类型并使用空参数包而不是 void。参数包可以同构地保存任意数量的类型,包括 0 和 1。然后它可以用于生成正确的类型和成员函数。您基本上只需要在每次使用 ... (link) 附近正确添加 Arg

#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> (link) 的 Event<> 添加一个简短的专业化来解决这个问题:

template<>
class Event<void> : public Event<>
{
    // Inherit constructors
    using Event<>::Event;
};

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。