是否有可能使用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。 该文档只有像vectorstringtypes的例子。

你需要使用:

 typedef std::map<int, int> map_type; map_type map = /* ... */; BOOST_FOREACH(const map_type::value_type& myPair, map) { // ... } 

原因是macros期望两个参数。 当您尝试内联对定义时,会引入第二个逗号,而不是使用macros三个参数。 预处理器不尊重任何C ++构造,只知道文本。

所以当你说BOOST_FOREACH(pair<int, int>, map) ,预处理器会看到macros的这三个参数:

1. pair<int
2. int>
3. map

哪个是错的 这是在for-each文档中提到的。

我使用了Boost的Range Ex库 ,它实现了一些精巧的范围适配器来迭代地图键或值。 例如:

 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"; 

你当然可以。 诀窍是,映射迭代器指向一对键和值。 它看起来像这样:

 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; } 

这是可能的,但这并不是真正做事情的最好方式(正如我以前提到的那样,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")); 

键入地图对是令人困惑的。 迭代映射的最简单的方法是使用一个元组(就像在python中一样):

 std::map<int, int> mymap; int key, value; BOOST_FOREACH(boost::tie(key, value), mymap) { ... } 

不要担心,这些逗号不会混淆预处理器,因为我在它们周围放置了括号。

我不喜欢每次想要在地图上使用foreach都被迫添加typedef的想法。 所以这里是我的基于boost foreach代码的实现:

 #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" ); 

是:

 typedef std::map<std::string,int> MyMap; MyMap myMap; BOOST_FOREACH(MyMap::value_type loop, myMap) { // Stuff } 

在C ++ 0x中,你可以更容易地做到:

 map<int, string> entries; /* Fill entries */ foreach(auto i, entries) cout << boost::format("%d = %s\n") % i.first % i.second;