如何在 std::map 中使用基于范围的 for() 循环?

Posted

技术标签:

【中文标题】如何在 std::map 中使用基于范围的 for() 循环?【英文标题】:How to use range-based for() loop with std::map? 【发布时间】:2011-08-06 00:12:29 【问题描述】:

C++11 基于范围的 for() 循环的常见示例总是这样简单:

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

     std::cout << xyz << std::endl;

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

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+ 中,所以我还没有机会尝试。)

【问题讨论】:

范围 for 语句与标准库 std::beginstd::end 函数或同名成员函数进行了一场邪恶的舞蹈。 @will 在一个 3 行示例中,您是否被假变量名所困扰? 【参考方案1】:

容器的每个元素都是一个map&lt;K, V&gt;::value_type,对于std::pair&lt;const K, V&gt;,它是一个typedef。因此,在 C++17 或更高版本中,您可以编写

for (auto& [key, value]: myMap) 
    std::cout << key << " has value " << value << std::endl;

或作为

for (const auto& [key, value]: myMap) 
    std::cout << key << " has value " << value << std::endl;

如果您不打算修改这些值。

在 C++11 和 C++14 中,您可以使用增强的 for 循环自行提取每一对,然后手动提取键和值:

for (const auto& kv : myMap) 
    std::cout << kv.first << " has value " << kv.second << std::endl;

如果您想要只读的值视图,也可以考虑标记kv 变量const

【讨论】:

相关链接在我研究后发现真的很有帮助,按照从最有帮助到最没有帮助的顺序:1) [最优秀的文章]thispointer.com/how-to-iterate-over-an-unordered_map-in-c11, 2) [优秀的一般迭代器信息]cplusplus.com/reference/iterator, 3) [cplusplus.com std::unordered_map 参考 pg]cplusplus.com/reference/unordered_map/unordered_map, 4) [cppreference.com std::unordered_map 参考 pg]en.cppreference.com/w/cpp/container/unordered_map, 5) 另见my answer here。跨度> 【参考方案2】:

在 C++17 中,这称为 structured bindings,它允许以下内容:

std::map< foo, bar > testing =  /*...blah...*/ ;
for ( const auto& [ k, v ] : testing )

  std::cout << k << "=" << v << "\n";

【讨论】:

是否有可能获得一个const &amp; 到键,但一个非常量引用的值? (因为这就是 map::value_type 所做的......) @peterchen: kconst 如果你使用for(auto&amp;[k,v]:testing) 结构化绑定上的cpppreference en.cppreference.com/w/cpp/language/structured_binding 如果您使用 GCC 编译,则需要版本 7 或更高版本才能进行结构化绑定:gcc.gnu.org/projects/cxx-status.html【参考方案3】:

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

for( type-specifier-seq simple-declarator : expression ) statement

在语法上等价于


    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-specifier-seq simple-declarator(*begin);
        statement
    

所以您可以清楚地看到,在您的情况下,abc 将是 std::pair&lt;key_type, value_type &gt;。 因此,对于打印,您可以通过 abc.firstabc.second 访问每个元素

【讨论】:

【参考方案4】:

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

for (const auto& value : myMap | boost::adaptors::map_values)

    std::cout << value << std::endl;

有一个等效的 boost::adaptors::key_values

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

【讨论】:

【参考方案5】:

如果 foo 和 bar 的复制赋值运算符很便宜(例如 int、char、pointer 等),您可以执行以下操作:

foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)

  cout << "Foo is " << f << " Bar is " << b;

【讨论】:

第一个 sn-p 代码没有使用“C++11 基于范围的 for()”。这不是对“C++11:如何将基于范围的 for() 循环与 std::map 一起使用?”的答案 @ytj 答案中已经提到它不起作用。我不想删除它,以便新用户不必尝试并再次发现事实。

以上是关于如何在 std::map 中使用基于范围的 for() 循环?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用基于范围的 for 循环修改地图中的值?

我可以使用基于范围的 for 循环轻松迭代地图的值吗?

在临时字符串上使用基于范围的循环时为空字符?

如何使用单个 for 循环遍历 std::map<string,int> 和 std::vector<int>?

std :: unordered_map向量下标超出范围

如何检测 std::map 循环中的最后一次迭代?