48

Is it possible to iterate over all of the values in a std::map using just a "foreach"?

This is my current code:

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

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

Is there a way I can do the following?

for (MyClass* i : /*magic here?*/) {
    i->bar() ;
}
honk
  • 9,137
  • 11
  • 75
  • 83
Blacklight Shining
  • 1,468
  • 2
  • 11
  • 28

4 Answers4

49

From C++1z/17, you can use structured bindings:

#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;
}
Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • 5
    structured bindings are awesome, but I compile with -Werror and unused-variable cries about the key not being used a lot :) – galois Feb 16 '18 at 22:21
  • 1
    Try `[[maybe_unused]]` attribute. See here https://stackoverflow.com/q/41404001/580083 – Daniel Langr Feb 17 '18 at 07:27
  • @galois and @ Daniel, Sorry if this is a basic question but what exactly is ***Structured Bindings***? Thanks a lot in advance! – Milan Jun 25 '21 at 17:06
  • @Milan Have you tried to google it? There are tons of explanations all around. – Daniel Langr Jun 26 '21 at 04:58
  • 1
    @DanielLangr I was reading an answer and as soon as I read a new word, I just commented here. But you are right... I should have googled it first. Sorry about that. (For the future readers: I found this short & simple video on *Structured Bindings* quite useful: https://youtu.be/eUsTO5BO3WI) – Milan Jun 28 '21 at 16:44
33
std::map<float, MyClass*> foo;

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

in c++11 (also known as c++0x), you can do this like in C# and Java

Sungguk Lim
  • 6,109
  • 7
  • 43
  • 63
lovaya
  • 445
  • 3
  • 3
  • What do you mean by “you can do this”? Are you referring to ranged-based for loops in general? – Blacklight Shining Oct 26 '12 at 12:54
  • YES! The new C++ Standard c++11 import a new for-range syntax feature,It's easier to iterator elments in containers.for example: vector vec{0, 1, 2 ,3, 4, 5, 6, 7, 8, 9};now in c++11,you can wirte code like this: for(auto any : vec) { cout << any << endl;}, you can compiler this code in visual c++ 2012, or in g++ 4.6 or higher with the argument -std=c++0x – lovaya Oct 26 '12 at 18:15
  • 8
    Well…this is not quite helpful, as I knew about ranged-based loops before posting…my question, which @Xeo has already answered, is about how to iterate through just the values of a map, without a line like `MyClass *j = any.second ;` :P – Blacklight Shining Oct 26 '12 at 18:28
  • I understand auto, but why const auto&? Is it not possible to edit the variable while in loop? – Meet Taraviya Mar 03 '17 at 04:52
23

The magic lies with Boost.Range's map_values adaptor:

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

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

And it's officially called a "range-based for loop", not a "foreach loop". :)

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 1
    i would have written `auto const&&`. – Cheers and hth. - Alf Oct 26 '12 at 12:46
  • @Cheersandhth.-Alf I think you mean `auto const &&i` :) – Blacklight Shining Oct 26 '12 at 12:46
  • @Xeo /ranged-based for loop/. I knew that. I must've been looking at SO tags too long… 6_9 Anyway, thank you! This is just what I was looking for. Can you explain why the pipe there is valid? That's a bitwise `or`, right? – Blacklight Shining Oct 26 '12 at 12:49
  • 1
    @Blacklight: Boost.Range's adaptors overload the `operator|` to make chaining easier: `map | map_values | filtered(pred) | transformed(blub) | reversed`. :) – Xeo Oct 26 '12 at 12:53
  • @Xeo `filtered()`? `transformed()`? `reversed`? Looks like I have a lot to read now… Good to know all of these exist! – Blacklight Shining Oct 26 '12 at 12:55
  • 2
    @Blacklight: [It's all there in the documentation!](http://www.boost.org/libs/range/doc/html/range/reference/adaptors/reference.html) :) – Xeo Oct 26 '12 at 12:58
  • @Cheersandhth.-Alf: Doesn't `auto const&&` only bind to rvalue references specifically, while `auto&&` is a "universal" reference (can bind to lvalues as well)? – Claudiu Jul 14 '15 at 15:55
  • @Claudiu: Yes. My first comment has one ampersand too many, and my second has one too few. It appears that some commentary here has been removed, since the second comment evidently refers to something. Maybe the first one also referred to something. A lost context. – Cheers and hth. - Alf Jul 14 '15 at 16:23
13

Since C++20 you can add the range adaptor std::views::values from the Ranges library to your range-based for loop. This way you can implement a similar solution to the one in Xeo's answer, but without using Boost:

#include <map>
#include <ranges>

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

for (auto const &i : foo | std::views::values)
    i->bar();

Code on Wandbox

honk
  • 9,137
  • 11
  • 75
  • 83
  • Even though I like the syntactic sugar of the "pipe" symbole here very much, the more obvious implementation would say `for (auto const& it : std::views::values(foo)) it->bar();` – King Thrushbeard Apr 19 '23 at 09:09
  • 1
    I agree, with only one range adapter, the better syntax is a matter of preference. But the idea is that you can chain multiple range adapters together, and then the piping concept makes more sense. – honk Apr 19 '23 at 09:37