【问题标题】:Passing a Functor as a param to a method将 Functor 作为参数传递给方法
【发布时间】:2011-03-28 20:08:27
【问题描述】:

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

//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...我认为这完全有可能,无需将这个框架拖入其中。

谢谢!

【问题讨论】:

    标签: c++ templates functor


    【解决方案1】:

    这可能是最直接的方式:

    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:也许吧。看它只用在一行代码中,可能没有意义。
    【解决方案2】:

    如果我理解正确,你想要的是std::bind2nd

    std::for_each(_objects.begin(), _objects.end(), 
                  std::bind2nd(std::mem_fun_ref(&Subscriber::receive), event));
    

    成员函数Subscriber::receive 有两个参数。第一个是隐含的this 指针,第二个是const Event &amp;std::bind2nd,给定一个带有两个参数的函数f,返回一个带有一个参数的函数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 对象。这将在带有std::bind 的 C++0x 中工作,并且使调度函数成为模板参数的想法仍然有效。您甚至可以使用特征,使该机制更加灵活。

    【讨论】:

    • 是的,这看起来像我想要做的,但你能重写这个答案以使 mem_fun() 成为我可以传递给 iterate 方法的东西吗?现在,您已经在 for_each 方法中硬编码了订阅者的接收方法...谢谢!
    • 这里漏掉的问题是T可能不是Subscriber,而是Dispatcher。现在你的代码不起作用。您可以使用重载解析来关闭类型(我的回答),或者创建两个iterate 函数并使用enable_if 禁用其中一个基于T;但这需要 Boost 或 C++0x。
    【解决方案3】:

    您可以创建一个函子类来包装您的 Subscriber 和 Dispatcher 类,例如

    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() 来获得一个“真正的”函子,但你明白了。

    干杯

    【讨论】:

      【解决方案4】:

      检查您是否有 tr1。如果你有 tr1,它包含 std::bind,它几乎完全等同于 boost 实现。这应该在&lt;functional&gt; 标头中找到。

      如果您没有 tr1,请考虑使用 Boost。我强烈建议至少使用boost::bind,因为它是轻量级的并且只有标题。

      如果您没有 tr1 并且不会使用 Boost,则您想混合使用 bind2ndmem_fun1。第一个绑定第二个参数(在这种情况下,您的事件;对象将是第一个)并且mem_fun1mem_fun 相同,但它需要两个参数,一个要调用的对象和一个要传递的参数被调用的成员函数。不过,这完全是一团糟。

      如果您确实可以访问bind,这相当容易。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-16
        • 1970-01-01
        • 2015-12-26
        • 1970-01-01
        • 2011-08-18
        • 2018-08-08
        相关资源
        最近更新 更多