6

当多路复用对许多子对象的调用时,防止循环样板代码的优雅方法是什么?

问题举例说明:

struct Foo {
  void Boo();
  void Hoo();
  bool IsActivated();
};

struct FooAggregator {
  ...
  void Boo();
  void Hoo();
  ...
  std::vector<Foo> m_foos;
};

FooAggregator::Boo() {
  for(size_t i=0, e=m_foos.size(); i!=e; ++i) {
    if(m_foos[i].IsActivated()) {
      m_foos[i].Boo();
    }
  }
}

FooAggregator::Hoo() {
  for(size_t i=0, e=m_foos.size(); i!=e; ++i) {
    if(m_foos[i].IsActivated()) {
      m_foos[i].Hoo();
    }
  }
}

如您所见,FooAggregator 实现与单个 Foo 相同(相似)的接口,迭代所有 Foo 对象,调用它们各自的成员函数。

如您所见,迭代循环是完整的样板文件,对 FooAggregator 的每个成员函数重复。

什么是从 FooAggregators 成员函数的实现中删除样板的优雅方法

4

4 回答 4

6

您可以Boost.Bind 按照@Space_C0wb0y 的建议使用。但是,如果您出于某种原因无法使用它,那么您可以执行以下操作:

struct FooAggregator 
{
    typedef void (Foo::*Fun)();


    void Boo() {  CallForEach(m_foos.begin(), m_foos.end(), &Foo::Boo); }
    void Hoo() {  CallForEach(m_foos.begin(), m_foos.end(), &Foo::Hoo); }

    template<typename FwdIterator>
    void CallForEach(FwdIterator first, FwdIterator last, Fun fun)
    {
        while (first != last ) 
        { 
            if(first->IsActivated())
            {
                 (first->*fun)();
            }
            first++;
        }
    }
};

或者您可以使用std::for_eachfrom<algorithm>作为:

#include <algorithm>

struct FooAggregator 
{
    typedef void (Foo::*Fun)();

    void Boo() {  std::for_each(m_foos.begin(), m_foos.end(), Call(&Foo::Boo)); }
    void Hoo() {  std::for_each(m_foos.begin(), m_foos.end(), Call(&Foo::Hoo)); }

    struct Call
    {
        Fun m_fun;
        Call(Fun fun) : m_fun(fun) {}
        void operator()(Foo & foo)
        {
            if(foo.IsActivated())
            {
               (foo.*m_fun)();
            }
        }
   };    
};

阅读Function 对象以了解第二个示例。


在 C++0x(即 C++11)中,它非常简单。您可以将 lamdastd::for_each用作:

#include <algorithm>

struct FooAggregator 
{
    void Boo()
    {  
         std::for_each(m_foos.begin(), m_foos.end(), [](Foo &foo){ if (foo.IsActivated()) foo.Boo(); } ); 
    }

    void Hoo()
    {  
         std::for_each(m_foos.begin(), m_foos.end(), [](Foo &foo){ if (foo.IsActivated()) foo.Hoo(); } ); 
    }
    //other code
};
于 2011-05-20T09:18:52.083 回答
1

您可以使用Boost.Bind将对象传递给boost::function指定要调用的方法的调度方法。那么您只需要一个可以使用不同目标方法作为参数调用的调度方法。

于 2011-05-20T09:11:06.353 回答
1

我将采用 Nawaz 的第一个很好的例子并简化一些:

(记住,我想减少样板,而不是介绍最花哨的功能。)

// FooAggregator.h
struct FooAggregator {
    template<typename MemFn>
    void CallForEachFoo(MemFn fun);

    void Boo();
    void Hoo();
};

// FooAggregator.cpp
template<typename MemFn>
void FooAggregator::CallForEachFoo(MemFn fun) {
    BOOST_FOREACH(Foo& o, m_foos) {
      if(o.IsActivated()) {
        (o.*fun)();
      }
    }
}

void Boo() {  CallForEachFoo(&Foo::Boo); }
void Hoo() {  CallForEachFoo(&Foo::Hoo); }
于 2011-05-21T19:12:53.117 回答
0

Nawaz 的回答很有趣,但还有其他解决方案。

首先,您应该认识到您的聚合器在很大程度上是一种Composite模式。

其次,我会选择:

  • 外部迭代
  • 一个for_each类似函数的成员方法(实际上是 2,因为const重载)。

对于外部迭代,请继续阅读:)

相对不幸的是,C++ 迭代器语法并不真正适合“跳过”迭代器,但它仍然是可以实现的。

class ActiveIterator {
public:
  friend class FooAggregator;

  friend bool operator==(ActiveIterator lhs, ActiveIterator rhs) {
    return lhs._it == rhs._it;
  }

  ActiveIterator& operator++() {
    this->next();
    return *this;
  }

  Foo* operator->() const { return _it::operator->(); }
  Foo& operator*() const { return *_it; }

private:
  typedef std::vector<Foo>::iterator base;
  ActivateIterator(base begin, base end): _it(begin), _end(end) {
    if (_it == _end || _it->IsActive()) { return; }
    this->next();
  }

  void next() {
    ++it; while (_it != _end && !_it->IsActive()) { ++_it; }
  }

  base _it, _end;
};

然后,您的聚合只是具有BeginEnd方法,并由调用者与您的迭代器进行交互。

注意:您可以使其模板一次性拥有可变/常量实现

但是,外部迭代仍然非常庞大,因为 C++ 缺少使事情变得简单的生成器语法。

于 2011-05-20T12:19:15.013 回答