C++ 模板化委托,如 Unity

C++ Templated Delegates like Unity

看到 Unity 的委托和事件后,我正在尝试编写自己的代码: 我想创建一个带有可变参数模板的 class,以指定函数的 return 类型和可选参数。

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...);
    }
};

如您所见,我希望 class 以这种方式使用:

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

和 "adding" 函数分别使用 += 运算符,例如:

voidDelegate += voidFunc;
//etc...

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

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

lambda 函数是正确的:它接受一个 int 和 return void,所以我不明白哪里错了。

问题是您的 std::vector 存储了函数指针。它不存储 std::bind 对象,不存储成员函数,不存储仿函数,也不存储 lambda。您正在尝试向其中添加一个 lambda,因此失败了。

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

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

Demo

顺便说一句,您可以通过完美转发您的 operator() 参数并在界面中复制您的 newElement 参数并将其移动到 std::vector.

您的委托只接受函数指针。 lambda 不是函数指针。但是,您尝试使用的 lambda 不会捕获任何内容。这意味着由于 some sorcery:

它可以转换为函数指针
MyDelegate<void, int> delegate1;
delegate1 += +[](const int a)->void{std::cout << a << std::endl; };
             ↑↑↑

但是,一旦您想允许具有成员变量的仿函数,额外的 + 将不起作用:

delegate1 += +[x](const int a) { ... }; // error: no match for operator+

到那个时候你一定要使用 std::function 的建议。

@TartanLlama 说的对,std::function就是你需要的

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