将一个函数对象作为参数传递给方法

3

我有以下两个类展示了命令模式。(不幸的是,两个类都有一个独特的方法名。)

//pseudocode 
class Subscriber {
    virtual void receive(const Event&) = 0;
}

class Dispatcher {
    virtual void dispatch(const Event&) = 0;
}

我有一个类模板,其中包含一种类型的列表,并带有一种迭代此列表的方法。

//pseudocode
template<typename T>
class Registry {
    typedef list<T> ObjectList;
    ObjectList _objects;
    void iterate(const Event& event) {
        for_each(_objects.begin(), _objects.end(), ...);  //not sure what to do here
    }
}

我希望使用mem_fun来创建一个函数对象,该对象在适当时调用receivedispatch。我能够创建一个简单的用例,在其中直接调用没有任何参数的方法,例如:

//pseudocode
class Simple {
    void simple() {/*...*/}
}

然后我进行迭代:

for_each(_objects.begin(), _objects.end(), mem_fun(&Simple::simple);

很遗憾,我不知道如何将event参数传递给mem_fun。查看头文件,似乎可以传递一个单一的参数,但我对C ++不是很熟悉,不理解我需要做什么。

最终,我希望使iterate方法接受一种函数对象类型,以便在列表中的每个方法上触发该方法。

我更喜欢避免使用Boost......我认为这完全可以在不引入该框架的情况下实现。

谢谢!

4个回答

6
这可能是最直接的方法:
class event_forwarder // make private in Registry
{
public:
    event_forwarder(const Event& event) :
    mEvent(event)
    {}

    void operator()(Subscriber& subcriber) const
    {
        subscriber.receive(mEvent);
    }

    void operator()(Dispatcher& dispatcher) const
    {
        dispatcher.dispatch(mEvent);
    }

private:
    const Event& mEvent;
};

然后:

for_each(_objects.begin(), _objects.end(), event_forwarder(event));

假设 C++0x 或 TR1 功能不可用,我喜欢这个解决方案,但构造函数应该是 explicit,并且该类应该有一个公共的 typedef void result_type; - ildjarn
@ildjarn:也许吧。考虑到它只在一行代码中使用,可能没有必要。 - GManNickG

4
如果我理解正确,您想要的是 std::bind2nd:
std::for_each(_objects.begin(), _objects.end(), 
              std::bind2nd(std::mem_fun_ref(&Subscriber::receive), event));

成员函数Subscriber::receive有两个参数。第一个是隐式的this指针,第二个是const Event &。给定一个接受两个参数的函数fstd::bind2nd返回一个接受一个参数的函数f_1,它使用第二个参数的固定值调用原始函数f编辑: 为了处理分配函数的不同名称,可以将分配函数作为模板参数:
//pseudocode
template<typename T, void (T::*dispatch_method)(Event)>
class Registry {
    typedef list<T> ObjectList;
    ObjectList _objects;
    void iterate(const Event& event) {
        std::for_each(_objects.begin(), _objects.end(), 
                      std::bind2nd(std::mem_fun_ref(dispatch_method), event));
    }
}

很不幸,似乎没有办法使bind2nd处理const引用参数,所以我的整个解决方案都是无效的,除非复制Event对象对您来说没问题。但在C++0x中使用std::bind可以解决这个问题,而将分派函数作为模板参数的想法仍然有效。您甚至可以使用特性,使该机制更加灵活。


是的,那看起来就是我想做的,但你能否重写这个答案,使mem_fun()成为我可以传递到iterate方法中的内容?现在,你已经在for_each方法中硬编码了Subscriber的receive方法...谢谢! - Tim Reddy
这里遗漏的问题是 T 可能不是 Subscriber,而是 Dispatcher。现在你的代码无法工作。你可以使用重载分辨来基于类型切换(我的答案),或者创建两个 iterate 函数并使用 enable_if 基于 T 禁用其中一个;但这需要 Boost 或 C++0x。 - GManNickG

0
你可以创建一个函数对象类,将你的订阅者和分发者类包装起来,例如:
class MyFunctor {
  public:
    virtual void Execute(const Event& event) = 0;
};

class MySubscriberFunctor : public MyFunctor {
  private:
    Subscriber subscriber_;
  public:
    void Execute(const Event& event) {
      subscriber_.receive(event);    
    }
};

class MyDispatcherFunctor : public MyFunctor {
  private:
    Dispatcher dispatcher_;
  public:
    void Execute(const Event& event) {
      dispatcher_.dispatch(event);    
    }
};

你的对象列表可以将这些函数包装器存储为MyFunctor实例列表。这样,您可以在它们上调用Execute()并让它们委托给底层类。您应该使用operator()而不是Execute()来获取一个“真正”的函数对象,但您已经明白了。

祝好


0

检查是否有tr1。如果您有tr1,则包含std :: bind,它几乎完全等同于boost实现。这应该在<functional>头文件中找到。

如果您没有tr1,请考虑使用Boost。我强烈建议至少使用boost :: bind,因为它很轻量级且仅限头文件。

如果您没有tr1并且不使用Boost,则需要混合使用bind2ndmem_fun1。第一个绑定第二个参数(在本例中为事件;对象将是第一个),而mem_fun1mem_fun相同,但它期望两个参数,要调用的对象和传递成员函数的一个参数。这是一团糟。

如果您可以访问bind,那么就很容易了。

for_each(objects.begin(), objects.end(), bind(&Simple::simple, _1, event))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接