是否可以用C++实现事件?

14 c++ templates class operator-overloading

我想在C++中实现一个C#事件,看看我能否做到.我卡住了,我知道底部是错的,但我发现我最大的问题是......

在这种情况下,如何将()操作员重载为任何内容?我不能?我可以吗?我可以实施一个好的选择吗?Tint func(float)

#include <deque>
using namespace std;

typedef int(*MyFunc)(float);

template<class T>
class MyEvent
{
    deque<T> ls;
public:
    MyEvent& operator +=(T t)
    {
        ls.push_back(t);
        return *this;
    }
};
static int test(float f){return (int)f; }
int main(){
    MyEvent<MyFunc> e;
    e += test;
}
Run Code Online (Sandbox Code Playgroud)

Jam*_*lis 23

如果您可以使用Boost,请考虑使用Boost.Signals2,它提供信号槽/事件/观察器功能.它简单易用,非常灵活.Boost.Signals2还允许您注册任意可调用对象(如仿函数或绑定成员函数),因此它更灵活,并且它具有许多功能,可帮助您正确管理对象生存期.


如果您正在尝试自己实施它,那么您就走在了正确的轨道上.但是,您遇到了一个问题:您确实想要对每个已注册函数返回的值做什么?您只能返回一个值operator(),因此您必须决定是否要返回任何内容,或者其中一个结果,或以某种方式汇总结果.

假设我们想要忽略结果,实现它非常简单,但如果将每个参数类型作为单独的模板类型参数(或者,您可以使用Boost.TypeTraits,它允许您使用轻松剖析函数类型):

template <typename TArg0>
class MyEvent
{
    typedef void(*FuncPtr)(TArg0);
    typedef std::deque<FuncPtr> FuncPtrSeq;

    FuncPtrSeq ls;
public:
    MyEvent& operator +=(FuncPtr f)
    {
        ls.push_back(f);
        return *this;
    }

    void operator()(TArg0 x) 
    { 
        for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
            (*it)(x);
    }
};
Run Code Online (Sandbox Code Playgroud)

这要求注册函数具有void返回类型.为了能够接受任何返回类型的函数,您可以更改FuncPtr

typedef std::function<void(TArg0)> FuncPtr;
Run Code Online (Sandbox Code Playgroud)

(或使用boost::functionstd::tr1::function如果您没有可用的C++ 0x版本).如果您确实想要对返回值执行某些操作,可以将返回类型作为另一个模板参数MyEvent.这应该是相对简单的.

通过上述实现,以下应该有效:

void test(float) { }

int main() 
{
    MyEvent<float> e;
    e += test;
    e(42);
}
Run Code Online (Sandbox Code Playgroud)

另一种允许您支持不同事件的方法,就是对函数类型使用单个类型参数,并且有多个重载operator()重载,每个重载都使用不同数量的参数.这些重载必须是模板,否则您将遇到任何与事件的实际arity不匹配的重载的编译错误.这是一个可行的例子:

template <typename TFunc>
class MyEvent
{
    typedef typename std::add_pointer<TFunc>::type FuncPtr;
    typedef std::deque<FuncPtr> FuncPtrSeq;

    FuncPtrSeq ls;
public:
    MyEvent& operator +=(FuncPtr f)
    {
        ls.push_back(f);
        return *this;
    }

    template <typename TArg0>
    void operator()(TArg0 a1) 
    { 
        for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
            (*it)(a1);
    }

    template <typename TArg0, typename TArg1>
    void operator()(const TArg0& a1, const TArg1& a2)
    {
        for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
            (*it)(a1, a2);
    }
};  
Run Code Online (Sandbox Code Playgroud)

(我在std::add_pointer这里使用过C++ 0x,但是这个类型修饰符也可以在Boost和C++ TR1中找到;它只是使得使用函数模板更简洁,因为你可以直接使用函数类型;你不要必须使用函数指针类型.)这是一个用法示例:

void test1(float) { }
void test2(float, float) { }

int main()
{
    MyEvent<void(float)> e1;

    e1 += test1;
    e1(42);

    MyEvent<void(float, float)> e2;
    e2 += test2;
    e2(42, 42);
}
Run Code Online (Sandbox Code Playgroud)