Можно ли использовать boost:: foreach с std:: map?
Я нахожу boost:: foreach очень полезным, так как это экономит много письменности. Например, допустим, что я хочу напечатать все элементы в списке:
std::list<int> numbers = { 1, 2, 3, 4 };
for (std::list<int>::iterator i = numbers.begin(); i != numbers.end(); ++i)
cout << *i << " ";
boost:: foreach делает код намного проще:
std::list<int> numbers = { 1, 2, 3, 4 };
BOOST_FOREACH (int i, numbers)
cout << i << " ";
Гораздо лучше! Однако я никогда не понял способ (если это вообще возможно) использовать его для std::map
s. В документации есть только примеры с такими типами, как vector
или string
.
Ответы
Ответ 1
Вам нужно использовать:
typedef std::map<int, int> map_type;
map_type map = /* ... */;
BOOST_FOREACH(const map_type::value_type& myPair, map)
{
// ...
}
Причина заключается в том, что макрос ожидает два параметра. Когда вы пытаетесь встроить определение пары, вы вводите вторую запятую, вместо этого добавляя макрос три параметра. Препроцессор не уважает конструкции С++, он знает только текст.
Итак, когда вы говорите BOOST_FOREACH(pair<int, int>, map)
, препроцессор видит эти три аргумента для макроса:
1. pair<int
2. int>
3. map
Это неправильно. Это указано в документации для каждой.
Ответ 2
Я использую Boost Range Ex library, в которой реализованы некоторые адаптеры дальномерного диапазона для итерации над ключами или значениями карты. Например:
map<int, string> foo;
foo[3] = "three";
foo[7] = "seven";
BOOST_FOREACH(i, foo | map_keys)
cout << i << "\n";
BOOST_FOREACH(str, foo | map_values)
cout << str << "\n";
Ответ 3
Конечно. Однако трюк заключается в том, что итератор карты указывает пару ключей и значений. Он будет выглядеть примерно так:
typedef std::map<std::string, int> MapType;
MapType myMap;
// ... fill the map...
BOOST_FOREACH(MapType::value_type val, myMap)
{
std::cout << val.first << ": " << val.second << std::endl;
}
Ответ 4
Возможно, но это не лучший способ сделать что-то (как я уже упоминал несколько раз раньше, for_each почти никогда не бывает, а BOOST_FOREACH только немного лучше). Для вашего первого примера, я думаю, вам будет лучше:
std::copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, " "));
Он работает аналогично карте, за исключением того, что вам нужно определить оператор < < для него, поскольку уже не определено:
typedef map<std::string, int>::value_type vt;
std::ostream &operator<<(std::ostream &os, vt &v) {
return os << v.first << ": " << v.second;
}
... и еще раз std::copy
выполняет работу довольно хорошо:
std::copy(mymap.begin(), mymap.end(),
std::ostream_iterator<vt>(std::cout, "\n"));
Ответ 5
Наведение пары карт в заблуждение. Самый простой способ итерации карты - это кортеж (как и в python):
std::map<int, int> mymap;
int key, value;
BOOST_FOREACH(boost::tie(key, value), mymap)
{
...
}
И не волнуйтесь, эти запятые не будут путать препроцессор, потому что я разместил вокруг них круглые скобки.
Ответ 6
Мне не нравилась идея принудительно добавлять typedefs каждый раз, когда я хотел использовать foreach на карте. Итак, вот моя реализация на основе кода foreach boost:
#ifndef MUNZEKONZA_FOREACH_IN_MAP
#include <boost/preprocessor/cat.hpp>
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x) BOOST_PP_CAT(x, __LINE__)
namespace munzekonza {
namespace foreach_in_map_private {
inline bool set_false(bool& b) {
b = false;
return false;
}
}
}
#define MUNZEKONZA_FOREACH_IN_MAP(key, value, map) \
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin(); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();) \
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true; \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) && \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end(); \
(MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ? \
((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) : \
(void)0) \
if( munzekonza::foreach_in_map_private::set_false( \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \
for( key = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->first; \
!MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true) \
if( munzekonza::foreach_in_map_private::set_false( \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \
for( value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second; \
!MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)
Затем вы можете использовать его в своем коде: #define foreach_in_map MUNZEKONZA_FOREACH_IN_MAP
std::map<int, std::string> mymap;
mymap[0] = "oi";
mymap[1] = "noi";
std::map<int, std::string> newmap;
foreach_in_map(int key, const std::string& value, mymap) {
newmap[key] = value;
}
ASSERT_EQ( newmap.size(), 2 );
ASSERT_EQ( newmap.count(0), 1 );
ASSERT_EQ( newmap.count(1), 1 );
ASSERT_EQ( newmap.at(0), "oi" );
ASSERT_EQ( newmap.at(1), "noi" );
Вы также можете изменить значения: #define foreach_in_map MUNZEKONZA_FOREACH_IN_MAP
std::map<int, std::string> mymap;
mymap[0] = "oi";
mymap[1] = "noi";
std::map<int, std::string> newmap;
foreach_in_map(int key, std::string& value, mymap) {
value = "voronoi" + boost::lexical_cast<std::string>(key);
}
ASSERT_EQ( mymap.size(), 2 );
ASSERT_EQ( mymap.count(0), 1 );
ASSERT_EQ( mymap.count(1), 1 );
ASSERT_EQ( mymap.at(0), "voronoi0" );
ASSERT_EQ( mymap.at(1), "voronoi1" );
Ответ 7
Да:
typedef std::map<std::string,int> MyMap;
MyMap myMap;
BOOST_FOREACH(MyMap::value_type loop, myMap)
{
// Stuff
}
Ответ 8
В С++ 0x вы можете сделать следующее:
map<int, string> entries;
/* Fill entries */
foreach(auto i, entries)
cout << boost::format("%d = %s\n") % i.first % i.second;