45

I'd like to iterate over a std::map using BOOST_FOREACH and edit the values. I can't quite get it.

typedef std::pair<int, int> IdSizePair_t;
std::map<int,int> mmap;    
mmap[1] = 1;
mmap[2] = 2;
mmap[3] = 3;
BOOST_FOREACH( IdSizePair_t i, mmap )
    i.second++;
// mmap should contain {2,3,4} here

Of course this doesn't change anything because I'm not iterating by reference. So I substitute this line instead (as per the example in the Boost docs):

BOOST_FOREACH( IdSizePair_t &i, mmap )

and I get the compiler error:

error C2440: 'initializing' : 
cannot convert from 'std::pair<_Ty1,_Ty2>' to 'IdSizePair_t &'
    with
    [
        _Ty1=const int,
        _Ty2=int
    ]

Any suggestions?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
kmote
  • 16,095
  • 11
  • 68
  • 91
  • What compiler are you using? I tried your code on VS2008 and it worked correctly. I tested hvint's [answer](http://stackoverflow.com/questions/795443/using-boostforeach-with-stdmap/795482#795482) too and it worked. I'm using boost 1.36, if that matters. – Gustavo Muenz Apr 27 '09 at 22:25
  • you probably forgot the & ? without that, it copies the other pair, and constness won't matter then. – Johannes Schaub - litb Apr 27 '09 at 22:47

4 Answers4

69

The problem is with the first member of the pair, which should be const. Try this:

typedef std::map<int, int> map_t;
map_t mmap;  
BOOST_FOREACH( map_t::value_type &i, mmap )
    i.second++;
hvintus
  • 2,547
  • 21
  • 18
  • Thanks, hvint. That did it. Also (after reading your comment) I realized that another way to fix it is to change the first line of my original code to this: typedef std::pair IdSizePair_ty; (which allows me to iterate by reference) – kmote Apr 28 '09 at 15:47
  • kmote, yes, actually that is what i proposed in my answer (which i deleted when i saw hvint one's). Also, do you know why it behaves that way? I'll undelete mine if you need some explanation. – Johannes Schaub - litb Apr 28 '09 at 18:22
  • In answer to http://stackoverflow.com/questions/795443/using-boost-foreach-with-stdmap/2854319#2854319 "I get a compile error: error C2065: 'i' : undeclared identifier" I got the same error, until I realized that I had not `#include ` Once I added that the solution worked perfectly. It's odd you don't get an error saying BOOST_FOREACH is undeclared. – Jim Morris Mar 19 '11 at 06:19
  • It's interesting to note that the `typedef` is necessary, as (at least in g++ 4.8.5) leaving it out will make the preprocessor believe that three arguments have been passed to `BOOST_FOREACH` instead of just two (due to the comma in the template arguments to `std::map`). – Trebor Rude Jan 07 '16 at 19:40
21

This is an old thread, but there is a more convenient solution.

boost has the notion of 'range adapters' that perform a transformation on iterator ranges. There are specific range adapters for this exact use case (iterating over map keys or values): boost::adaptors::map_values and boost::adaptors::map_keys.

So you could iterate over map values like this:

BOOST_FOREACH(int& size, mmap | boost::adaptors::map_values)
{ 
    ++size;
}

More information here.

Alex Goldberg
  • 975
  • 11
  • 14
4

Another option is to use BOOST_FOREACH_PAIR, see my answer here:

BOOST_FOREACH & templates without typedef

Community
  • 1
  • 1
dtw
  • 1,785
  • 1
  • 13
  • 13
  • 1
    I like the look of BOOST_FOREACH_PAIR, but I don't see any official references to it and it's not in the 1.46 version I'm using. Did it ever get included into an official release? – Rian Sanderson Dec 09 '11 at 18:58
  • That was just a homemade macro - it was never implemented into boost. Ended as a wontfix (in favor of C++11 range-based loops) in https://svn.boost.org/trac/boost/ticket/3469 – baderous Sep 13 '16 at 12:27
0

As of C++11 consider using auto keyword:

std::map<int,int> mmap;    
mmap[1] = 1;
mmap[2] = 2;
mmap[3] = 3;

BOOST_FOREACH(auto& mpair, mmap)
    mpair.second++;

//mmap will contain {2,3,4} here
K.B
  • 1