像Unity一样的c++模板化委托

C++ Templated Delegates like Unity

本文关键字:c++ 一样 Unity      更新时间:2023-10-16

看到Unity的委托和事件后,我试图编写自己的代码:我想用可变模板创建一个类,指定函数的返回类型和可选参数。

template <typename Ret, typename... Args>
class MyDelegate{
    std::vector<Ret(*)(Args...)> vec;
    public:
    void operator+=( const Ret(*)(Args...)& newElement)
    {
        vec.push_back(newElement);
    }

    Ret operator()(const Args&... args)
    {
        for (auto i = vec.begin(); i != vec.end(); ++i)
            (*i)(args...);
    }
};

如你所见,我希望这样使用这个类:

MyDelegate<void> voidDelegate;
MyDelegate<void, int> intDelegate;
MyDelegate<int, char, boolt> miscDelegate;

和使用+=操作符"添加"函数,如:

voidDelegate += voidFunc;
//etc...

我现在有+=操作符的问题,因为VS不接受这个:

MyDelegate<void, int> delegate1;
delegate1 += [](const int a)->void{std::cout << a << std::endl; };

lambda函数是正确的:它接受int并返回void,所以我不明白什么是错的

问题是您的std::vector存储函数指针。它不存储std::bind对象,不存储成员函数,不存储函子,也不存储lambda。您试图向它添加一个lambda,因此失败。

如果你想存储任何类型的对象,支持调用正确的参数和返回类型,你需要std::function:

using FunType = std::function<Ret(Args...)>;
std::vector<FunType> vec;

演示

顺便说一下,您可以通过完美转发operator()参数并复制界面中的newElement参数并将其移动到std::vector中来改进您的解决方案。

您的委托只接受函数指针。lambda不是函数指针。但是,您尝试的lambda没有捕获任何内容。这意味着它可以通过一些魔法转换为函数指针:

MyDelegate<void, int> delegate1;
delegate1 += +[](const int a)->void{std::cout << a << std::endl; };
             ↑↑↑

然而,一旦你想允许有成员变量的函子,额外的+将不起作用:

delegate1 += +[x](const int a) { ... }; // error: no match for operator+
在这一点上,你肯定要使用TartanLlama的std::function的建议。

@TartanLlama的右边,std::函数就是你需要的。

调用循环可以折叠为for (auto handler : vec) handler(args...);