Как использовать BOOST_FOREACH с двумя std :: maps?

У меня есть код, который выглядит примерно так:

std::map<int, int> map1, map2;
BOOST_FOREACH(int i, map1)
{
    // do steps 1-5 here...
}
BOOST_FOREACH(int i, map2)
{
    // do steps 1-5 (identical to above) here...
}

Есть ли способ объединить карты, чтобы исключить повторяющийся код во втором цикле? Или способ расширить BOOST_FOREACH для перебора двух разных карт за один раз? Очевидно, я не хочу увеличивать временную сложность программы (иначе я мог бы просто создать новую карту и вставить в нее map1 и map2). У меня такое чувство, что я упускаю здесь что-то элементарное.


person kmote    schedule 20.04.2009    source источник
comment
это было специально, чтобы вы перебирали int? вы должны использовать пару ‹int, int›, а не только int. или у boost недавно появилась возможность перебирать только значение?   -  person Johannes Schaub - litb    schedule 21.04.2009
comment
Во всяком случае, я бы ожидал, что такая функция будет перебирать только ключ ... но она также не работает в моей версии Boost.   -  person ephemient    schedule 21.04.2009


Ответы (6)


Вы можете определить функцию:

typedef std::map<int, int> IntMap;

void doStuffWithInt(IntMap::value_type &i)
{
  // steps 1 to 5
}

BOOST_FOREACH(IntMap::value_type &i, map1)
  doStuffWithInt(i);
BOOST_FOREACH(IntMap::value_type &i, map2)
  doStuffWithInt(i);

Хотя в этом случае было бы еще проще использовать std::for_each:

for_each(map1.begin(), map1.end(), doStuffWithInt);
for_each(map2.begin(), map2.end(), doStuffWithInt);
person 1800 INFORMATION    schedule 20.04.2009
comment
Если вы сопоставляете целые числа с целыми числами (std :: map ‹int, int›), я считаю, что итераторы будут указывать на std :: pair ‹const int, int›, а не на простой int? - person David Rodríguez - dribeas; 21.04.2009
comment
да, наверное, на самом деле я этого не заметил - хотя, чтобы не запутать проблему, это не должно быть слишком сложно исправить - person 1800 INFORMATION; 21.04.2009

Идея здесь состоит в том, чтобы написать специальный тип итераторов для виртуального слияния двух контейнеров, что касается BOOST_FOREACH. Обратите внимание, что я не создаю новый контейнер из двух существующих. Я просто перехожу от end () первого контейнера к итератору begin () второго контейнера. Я не пробовал писать реальный класс merged_iterator, но, хотя писать его может немного долго, технически это не сложно. Я действительно удивлен, что не нашел что-то подобное с помощью Google. Но я недолго искал!

template<typename Container>
boost::iterator_range<
  merged_iterator<Container::iterator>
  >
concat_containers( Container& c1, Container& c2 )
{
  typedef merged_iterator<typename Container::iterator> MergedIterator;
  typedef boost::iterator_range<MergedIterator> IteratorRange;
  return IteratorRange(
    MergeIterator( c1.begin(), c1.end(), c2.begin(), c2.end() ),
    MergeIterator( c2.end(), c1.end(), c2.begin(), c2.end() ) );
}

// Now use a bit of magic to define merged_iterator<...>
// And you'll be able to write

BOOST_FOREACH( std::pair<int, int> i, concat_containers( map1, map2 ) )
{
// Do whatever you want here
}
person Benoît    schedule 20.04.2009
comment
Я реализовал это пару дней назад по другому вопросу. Обратите внимание, что это не доказано и может потребовать некоторой настройки: stackoverflow.com/questions/757153/ - person David Rodríguez - dribeas; 21.04.2009
comment
+1 за очень общий подход, например можно заставить работать даже с разными типами контейнеров. Но я думаю, что это может быть чрезмерно сложным для решения данной проблемы - просто сделайте предложенную функцию, например, 0800 ИНФОРМАЦИЯ! :) - person j_random_hacker; 21.04.2009
comment
Просто это перебор для этого конкретного вопроса. Я просто подумал, что могу попробовать и повеселиться :) - person Benoît; 21.04.2009

В дополнение к решению 1800 года, которое я бы порекомендовал, есть также различные хакерские решения:

for (int stage = 0; stage < 2; stage++) {
    BOOST_FOREACH(int i, stage == 0 ? map1 : map2) {
        ...
    }
}

typedef std::map<int, int> intmap;
std::vector<intmap *> v;
v.push_back(&map1);
v.push_back(&map2);
BOOST_FOREACH(intmap *m, v) {
    BOOST_FOREACH(int i, *m) {
        ...
    }
}

Примечание: когда я вижу, как коллеги пишут такой код, иногда меня одолевает непреодолимое желание задушить их. Используйте на свой риск.

person ephemient    schedule 20.04.2009
comment
+1 к сведению: вы всегда должны учитывать, что вы получаете, а что теряете. Если вы только получаете уплотнение кода и теряете читабельность, это не компенсирует. - person David Rodríguez - dribeas; 21.04.2009
comment
Я тоже проголосовал за, потому что мне нравятся оба ваших способа сделать это. первый довольно компактен и удобочитаем. второй тоже. хотя я бы изменил его на чтение intmap * v [] = {& map1, & map2}; BOOST_FOREACH (intmap * m, v) {...}. Я думаю, что в этом случае подойдут необработанные массивы. - person Johannes Schaub - litb; 21.04.2009

Самый простой способ такой:

std::map<int, int> map1, map2;
int key, value;
BOOST_FOREACH(boost::tie(key, value), boost::join(map1, map2))
{
    // do steps 1-5 here...
}

И не волнуйтесь, эти запятые не запутают препроцессор из-за скобок.

person Paul Fultz II    schedule 24.01.2012

С макушки я бы попробовал

std::map<int, int> map1, map2;
std::map<int, int>& maps = { map1, map2 }
BOOST_FOREACH(std::map<int, int> map, maps)
  BOOST_FOREACH(int i, map)
  {
      // do steps 1-5 here...
  }
person Kees-Jan    schedule 02.03.2010

Это объясняется здесь.

Ты можешь сделать это:

std::map<int,int> m;
typedef std::pair<int,int> pair_t;
BOOST_FOREACH(pair_t p, m)
person shashank.M    schedule 27.10.2011