34

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
  • 7,217
  • 11
  • 62
  • 65
Blacklight Shining
  • 1,258
  • 2
  • 10
  • 26

4 Answers4

32

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
  • 18,256
  • 1
  • 39
  • 74
  • 2
    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
25
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
  • 5,710
  • 6
  • 38
  • 60
lovaya
  • 365
  • 2
  • 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
  • 7
    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
20

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
  • 123,374
  • 44
  • 277
  • 381
  • 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
3

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
  • 7,217
  • 11
  • 62
  • 65