关于c ++:如何使用基于范围的for()循环与std :: map?

How to use range-based for() loop with std::map?

C++ 11基于距离的(f)循环的常见例子总是这样简单的:

1
2
3
4
5
std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7 };
for ( auto xyz : numbers )
{
     std::cout << xyz << std::endl;
}

在这种情况下,xyzint。但是,当我们有类似地图的东西时会发生什么呢?本例中的变量类型是什么:

1
2
3
4
5
6
std::map< foo, bar > testing = { /*...blah...*/ };
for ( auto abc : testing )
{
    std::cout << abc << std::endl;         // ? should this give a foo? a bar?
    std::cout << abc->first << std::endl;  // ? or is abc an iterator?
}

当要遍历的容器很简单时,看起来基于范围的for()循环将为我们提供每个项,而不是迭代器。这很好……如果它是迭代器,我们首先要做的就是取消对它的引用。

但当涉及到像地图和多重地图这样的事情时,我对它的预期感到困惑。

(我还在使用G++4.4,而基于范围的循环使用G++4.6+,所以我还没有机会尝试它。)


容器的每个元素都是一个map::value_type,对于std::pair,它是一个typedef。因此,你写这个

1
2
3
for (auto& kv : myMap) {
    std::cout << kv.first <<" has value" << kv.second << std::endl;
}

为了提高效率,最好将循环中的参数作为参考。如果需要值的只读视图,也可以考虑将其设置为const

从C++ 17开始,你也可以写

1
2
3
for (auto& [key, value]: myMap) {
    std::cout << key <<" has value" << value << std::endl;
}

更干净。


在C++ 17中,这被称为结构化绑定,它允许以下内容:

1
2
3
4
5
6
std::map< foo, bar > testing = { /*...blah...*/ };
for ( const auto& [ k, v ] : testing )
{
  std::cout << k <<"=" << v <<"
"
;
}


本文来源:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2049.pdf

for( type-speci?er-seq simple-declarator : expression ) statement

在语法上等价于

1
2
3
4
5
6
7
8
{
    typedef decltype(expression) C;
    auto&& rng(expression);
    for (auto begin(std::For<C>::begin(rng)), end(std::For<C>::end(rng)); begin != end; ++ begin) {
        type-speci?er-seq simple-declarator(*begin);
        statement
    }
}

所以你可以清楚地看到,在你的例子中,abc就是std::pair。因此,对于打印,您可以通过abc.firstabc.second访问每个元素。


如果您只想从地图中查看键/值,如使用Boost,则可以将Boost适配器与基于范围的循环一起使用:

1
2
3
4
for (const auto& value : myMap | boost::adaptors::map_values)
{
    std::cout << value << std::endl;
}

有一个等效的boost::adaptors::key_值

http://www.boost.org/doc/libs/1_51_0/libs/range/doc/html/range/reference/adaptors/reference/map_values.html


如果foo和bar的copy assignment操作符很便宜(例如int、char、pointer等),则可以执行以下操作:

1
2
3
4
5
foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)
{
  cout <<"Foo is" << f <<" Bar is" << b;
}