Вопрос

Я разрабатываю программу, в которой я часто так делаю:

void Model::SetCollideMode( const std::string &m )
{
  Body *body;

  std::map<std::string, Body* >::iterator iter;

  for (iter=this->bodies.begin(); iter!=this->bodies.end(); iter++)
  {
    body = iter->second;

    body->SetCollideMode( m );
  }
}

У меня есть несколько подобных методов в нескольких объектах, которые в основном применяют свойство ко всем его дочерним элементам. Исходя из мира Ruby, я хочу сделать что-то вроде:

for_all_bodies{ body->SetCollideMode(m) }

Есть ли способ сделать этот код более замыкательным, например или улучшить его?

Я осознаю, как работает C ++, что он основан на стеке, и в нем нет контекстной информации для создания идеального замыкания, подобного функциональности (для этого нужна виртуальная машина?), но, по крайней мере, по сравнению с текущим повторим этот код в 100 раз программирование.

Это было полезно?

Решение

Есть несколько подходов, но ни один из них не идеален.

Во-первых, «обычный» подход будет заключаться в том, чтобы определить функтор, чтобы делать то, что вам нужно:

struct SetCollideModeFunc {
  SetCollideModeFunc(const std::string& m) : m(m) {}
  void operator()(std::pair<std::string, Body*>& p) {
    Body* b = p.second;
    b->SetCollideMode(m);
  }

  const std::string& m;
};

void Model::SetCollideMode( const std::string &m )
{
  std::for_each(bodies.begin(), bodies.end(), SetCollideModeFunc(m));
}

Это не экономит много кода, но позволяет отделить итерацию от операции, которую вы хотите применить. И если вам нужно установить collidemode несколько раз, вы, конечно, можете использовать функтор.

Более короткая версия возможна с библиотекой Boost.Lambda, которая позволит вам определить встроенный функтор. Я не могу вспомнить точный синтаксис, так как я не часто использую Boost.Lambda, но это будет примерно так:

std::for_each(bodies.begin(), bodies.end(), _1.second->SetCollideMode(m));

В C ++ 0x вы получаете языковую поддержку лямбда-выражений, позволяющую использовать синтаксис, подобный этому, без использования сторонних библиотек.

Наконец, Boost.ForEach может быть опцией, допускающей такой синтаксис:

void Model::SetCollideMode(const std::string &m)
{
  BOOST_FOREACH ((std::pair<std::string, Body*> p), bodies) // note the extra parentheses. BOOST_FOREACH is a macro, which means the compiler would choke on the comma in the pair if we do not wrap it in an extra ()
  {
    p.second->SetCollideMode(m);
  }
}

Другие советы

В C ++ 0x да. Смотрите здесь. Как вы уже догадались, они сделаны в характерный для C ++ способ, т. е. если вы случайно закроете переменную стека, а затем оставите лямбда-объект дольше, чем стек, то у вас будет неопределенное поведение. Это совершенно новый способ аварийного завершения вашей программы! Но это несправедливо - во многих отношениях они более изощренны, чем лямбды во многих других языках, потому что вы можете объявить, в какой степени им разрешено изменять состояние.

До этого были попытки подражать тому же вещь , но они, вероятно, больше проблем, чем стоит.

BOOST_FOREACH (или новый цикл, основанный на диапазоне) - это, вероятно, верный путь, но вот как я обычно подхожу к лямбде в текущем стандарте, используя tr1 bind:

#include <algorithm>
#include <functional>
using namespace std;

void Model::SetCollideMode( const std::string &m )
{
  for_each(bodies.begin(),bodies.end(),
           tr1::bind(&Body::SetCollideMode,
                     tr1::bind(&pair<std::string, Body*>::second, _1), m));
}

Вы можете использовать Boost.Foreach .

#include <boost/foreach.hpp>

void Model::SetCollideMode(const std::string &m)
{
  typedef pair<std::string, Body*> body_t;
  BOOST_FOREACH (body_t& body, bodies)
  {
    body.second->SetCollideMode(m);
  }
}

C ++ пока не поддерживает лямбда-выражения. Я иногда использую этот обходной путь:

#include <boost/bind.hpp>
void Model::SetCollideMode( const std::string &m )
{    
  typedef std::map<std::string, Body* > Bodies;
  struct Helper
  {
      static SetCollideMode(const std::pair<std::string, Body*> & value,
                            const std::string & m)
      {
          value.second->SetCollideMode(m);
      }
  };

  for_each(bodies.begin(),
           bodies.end(),
           boost::bind(Helper::SetCollideMode,_1, m));
}

Просто мои 2 цента ..

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top