我能否使用基于范围的for循环轻松迭代映射的值?

48

是否可能仅使用“foreach”迭代遍历std::map中的所有值?

这是我的当前代码:

std::map<float, MyClass*> foo ;

for (map<float, MyClass*>::iterator i = foo.begin() ; i != foo.end() ; i ++ ) {
    MyClass *j = i->second ;
    j->bar() ;
}

有没有办法我可以做到以下事情?

for (MyClass* i : /*magic here?*/) {
    i->bar() ;
}

4
为什么你不喜欢 i->second->bar()? - Andriy
看一下这个问题的答案。我认为这正是你所需要的。 - Lyubomir Vasilev
4
@LyubomirVasilev:不,那个只是询问如何使用range-for循环遍历一般地图。这一个询问如何专门只遍历值。 - Xeo
@Xeo 哦,我明白了。我忽略了那个。 - Lyubomir Vasilev
4个回答

50

C++1z/17开始,你可以使用结构化绑定

#include <iostream>
#include <map>
#include <string>

int main() {
   std::map<int, std::string> m;

   m[1] = "first";
   m[2] = "second";
   m[3] = "third";

   for (const auto & [key, value] : m)
      std::cout << value << std::endl;
}

5
结构化绑定很棒,但我使用-Werror编译时,unused-variable会频繁报错说键没有被使用 :) - galois
1
尝试使用[[maybe_unused]]属性。请参见此处https://dev59.com/HVgR5IYBdhLWcg3wXcal - Daniel Langr
@galois 和 @Daniel,不好意思如果这是一个基础问题,但是什么是结构化绑定?非常感谢您们的帮助! - Milan
@Milan 你试过谷歌搜索吗?有很多解释都可以找到。 - Daniel Langr
1
@DanielLangr 我正在阅读一个答案,当我看到一个新词时,我就在这里发表了评论。但你是对的...我应该先谷歌一下。对此感到抱歉。(对于未来的读者:我发现这个关于结构化绑定的简短而简单的视频非常有用:https://youtu.be/eUsTO5BO3WI) - Milan

35
std::map<float, MyClass*> foo;

for (const auto& any : foo) {
    MyClass *j = any.second;
    j->bar();
}

在C++11(也称为C++0x)中,您可以像在C#和Java中一样实现这一点。


“你说的‘你可以这样做’是什么意思?你是指一般的基于范围的for循环吗?” - Blacklight Shining
是的!新的C++标准c++11引入了一个名为for-range的语法特性,它使得在容器中迭代元素更加容易。例如:vector<int> vec{0, 1, 2 ,3, 4, 5, 6, 7, 8, 9};现在在c++11中,你可以这样编写代码:for(auto any : vec) {cout << any << endl;},你可以在Visual C++ 2012或者在g++ 4.6或更高版本使用参数-std=c++0x编译该代码。 - lovaya
8
嗯...这并不是很有帮助,因为在发布之前我已经知道了基于范围的循环...我的问题,@Xeo已经回答了,是关于如何迭代地图中的值,而不需要像MyClass *j = any.second ;这样的代码行 :P - Blacklight Shining
我理解auto,但为什么需要用到const auto&?在循环中不能编辑变量吗? - Meet Taraviya

24

魔法在于Boost.Range的map_values适配器:

#include <boost/range/adaptor/map.hpp>

for(auto&& i : foo | boost::adaptors::map_values){
  i->bar();
}

它正式被称为“范围for循环”,而不是“foreach循环”。 :)


1
我本来会写成 auto const&& - Cheers and hth. - Alf
@Xeo /范围for循环/. 我知道的。我一定是看了太久的SO标签... 6_9 不管怎样,谢谢!这正是我在寻找的。你能解释一下为什么那里的管道符号是有效的吗?那是按位“或”,对吗? - Blacklight Shining
1
@Blacklight:Boost.Range的适配器重载了operator|,使得链式调用更加容易:map | map_values | filtered(pred) | transformed(blub) | reversed。 :) - Xeo
@Xeo filtered()transformed()reversed?看来我现在有很多东西要读了... 很高兴知道这些都存在! - Blacklight Shining
2
@Blacklight: 文档里都有! :) - Xeo
显示剩余3条评论

13

尽管我非常喜欢这里“管道”符号的语法糖,但更明显的实现应该是:for (auto const& it : std::views::values(foo)) it->bar(); - King Thrushbeard
1
我同意,只需要一个范围适配器,更好的语法是个人偏好的问题。但是这个想法是你可以将多个范围适配器链接在一起,然后管道概念就更有意义了。 - honk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接