C++ 是否可以将 boost::foreach 与 std::map 一起使用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2104208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it possible to use boost::foreach with std::map?
提问by Thomas Bonini
I find boost::foreachvery useful as it saves me a lot of writing. For example, let's say I want to print all the elements in a list:
我发现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 makes the code above much simplier:
boost::foreach 使上面的代码更加简单:
std::list<int> numbers = { 1, 2, 3, 4 };
BOOST_FOREACH (int i, numbers)
cout << i << " ";
Much better! However I never figured out a way (if it's at all possible) to use it for std::map
s. The documentation only has examples with types such as vector
or string
.
好多了!但是,我从来没有想出一种方法(如果可能的话)将它用于std::map
s。该文档仅包含类型为vector
或 的示例string
。
回答by GManNickG
You need to use:
您需要使用:
typedef std::map<int, int> map_type;
map_type map = /* ... */;
BOOST_FOREACH(const map_type::value_type& myPair, map)
{
// ...
}
The reason being that the macro expects two parameters. When you try to inline the pair definition, you introduce a second comma, making the macro three parameters instead. The preprocessor doesn't respect any C++ constructs, it only knows text.
原因是宏需要两个参数。当您尝试内联对定义时,您引入了第二个逗号,使宏改为三个参数。预处理器不尊重任何 C++ 构造,它只知道文本。
So when you say BOOST_FOREACH(pair<int, int>, map)
, the preprocessor sees these three arguments for the macro:
因此,当您说 时BOOST_FOREACH(pair<int, int>, map)
,预处理器会看到宏的这三个参数:
1.pair<int
2. int>
3. map
1. pair<int
2. int>
3.map
Which is wrong. This is mentionedin the for-each documentation.
这是错误的。这在 for-each 文档中提到。
回答by Manuel
I use Boost's Range Ex librarywhich implements some fancy range adaptors for iterating over map keys or values. For instance:
我使用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";
回答by Fred Larson
Sure you can. The trick is, however, that a map iterator points to a pair of the key and value. It would look something like this:
你当然可以。然而,诀窍是映射迭代器指向一对键和值。它看起来像这样:
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;
}
回答by Paul Fultz II
Typedefing a map pair is confusing. The most simplest way to iterate a map is with a tuple(just like in python):
对映射对进行类型定义令人困惑。迭代映射最简单的方法是使用元组(就像在 python 中一样):
std::map<int, int> mymap;
int key, value;
BOOST_FOREACH(boost::tie(key, value), mymap)
{
...
}
And don't worry, those commas won't confuse the preprocessor because I placed parenthesis around them.
别担心,这些逗号不会混淆预处理器,因为我在它们周围放置了括号。
回答by marko.ristin
I didn't like the idea to be forced to add typedefs each time I wanted to use a foreach on a map. So here is my implementation based on the boost foreach code:
我不喜欢每次我想在地图上使用 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)
Then you can use it in your code: #define foreach_in_map MUNZEKONZA_FOREACH_IN_MAP
然后你可以在你的代码中使用它:#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" );
You can also change the values: #define foreach_in_map MUNZEKONZA_FOREACH_IN_MAP
您还可以更改这些值:#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" );
回答by Jerry Coffin
It's possible, but it's not really the best way to do things (as I've mentioned a few times before, for_each almost never is, and BOOST_FOREACH is only marginally better). For your first example, I think you'd be better off with:
这是可能的,但它并不是最好的做事方式(正如我之前多次提到的,for_each 几乎从来都不是,而 BOOST_FOREACH 只是稍微好一点)。对于您的第一个示例,我认为您最好使用:
std::copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, " "));
It works pretty similarly with a map, except that you have to define operator<< for it, since there isn't one already defined:
它与地图的工作方式非常相似,除了您必须为其定义 operator<< ,因为还没有定义:
typedef map<std::string, int>::value_type vt;
std::ostream &operator<<(std::ostream &os, vt &v) {
return os << v.first << ": " << v.second;
}
...and once again, std::copy
does the job quite nicely:
...再一次,std::copy
这项工作做得很好:
std::copy(mymap.begin(), mymap.end(),
std::ostream_iterator<vt>(std::cout, "\n"));
回答by Martin York
Yes:
是的:
typedef std::map<std::string,int> MyMap;
MyMap myMap;
BOOST_FOREACH(MyMap::value_type loop, myMap)
{
// Stuff
}
回答by Thomas Bonini
In C++0x you can more easily do:
在 C++0x 中,您可以更轻松地执行以下操作:
map<int, string> entries;
/* Fill entries */
foreach(auto i, entries)
cout << boost::format("%d = %s\n") % i.first % i.second;