Is it possible to use boost::foreach with std::map?

Viewed 31225

I find boost::foreach very useful as it saves me a lot of writing. For example, let's say I want to print all the elements in a list:

std::list<int> numbers = { 1, 2, 3, 4 };
for (std::list<int>::iterator i = numbers.begin(); i != numbers.end(); ++i)
   cout << *i << " ";

boost::foreach makes the code above much simplier:

std::list<int> numbers = { 1, 2, 3, 4 };
BOOST_FOREACH (int i, numbers)
   cout << i << " ";

Much better! However I never figured out a way (if it's at all possible) to use it for std::maps. The documentation only has examples with types such as vector or string.

8 Answers

You need to use:

typedef std::map<int, int> map_type;
map_type map = /* ... */;

BOOST_FOREACH(const map_type::value_type& myPair, map)
{
    // ...
}

The reason being that the macro expects two parameters. When you try to inline the pair definition, you introduce a second comma, making the macro three parameters instead. The preprocessor doesn't respect any C++ constructs, it only knows text.

So when you say BOOST_FOREACH(pair<int, int>, map), the preprocessor sees these three arguments for the macro:

1.pair<int
2. int>
3. map

Which is wrong. This is mentioned in the for-each documentation.

I use Boost's Range Ex library which implements some fancy range adaptors for iterating over map keys or values. For instance:

map<int, string> foo;
foo[3] = "three";
foo[7] = "seven";

BOOST_FOREACH(i, foo | map_keys)
   cout << i << "\n";


BOOST_FOREACH(str, foo | map_values)
   cout << str << "\n";

Sure you can. The trick is, however, that a map iterator points to a pair of the key and value. It would look something like this:

typedef std::map<std::string, int> MapType;
MapType myMap;

// ... fill the map...

BOOST_FOREACH(MapType::value_type val, myMap)
{
    std::cout << val.first << ": " << val.second << std::endl;
}

It's possible, but it's not really the best way to do things (as I've mentioned a few times before, for_each almost never is, and BOOST_FOREACH is only marginally better). For your first example, I think you'd be better off with:

std::copy(numbers.begin(), numbers.end(), 
          std::ostream_iterator<int>(std::cout, " "));

It works pretty similarly with a map, except that you have to define operator<< for it, since there isn't one already defined:

typedef map<std::string, int>::value_type vt;

std::ostream &operator<<(std::ostream &os, vt &v) { 
    return os << v.first << ": " << v.second;
}

...and once again, std::copy does the job quite nicely:

std::copy(mymap.begin(), mymap.end(), 
          std::ostream_iterator<vt>(std::cout, "\n"));

Yes:

typedef std::map<std::string,int>    MyMap;

MyMap    myMap;

BOOST_FOREACH(MyMap::value_type loop, myMap)
{ 
       // Stuff
}
Related