像Unity这样的C ++ Templated代理

时间:2015-07-10 14:56:57

标签: c++ templates delegates variadic

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

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,所以我不明白什么是错的。

3 个答案:

答案 0 :(得分:3)

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

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

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

Demo

顺便提一下,您可以通过完善转发operator() args并在界面中复制newElement arg并将其移至std::vector来改善您的解决方案。

答案 1 :(得分:1)

您的委托只接受函数指针。 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的{​​{3}}建议。

答案 2 :(得分:0)

@ TartanLlama&right-right,std :: function就是你所需要的。

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