Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

converting a for loop to a std::for_each

Tags:

c++

foreach

I've got this for loop:

    std::vector<itemPtr>::iterator it;
    for(it=items.begin(); it!=items.end(); ++it)
    {
        investigators.addToLeaderInventory(*it);
    }

I'd like to convert it to something like this:

std::for_each(items.begin(), items.end(), investigators.addToLeaderInventory);

However, that line doesn't compile. g++ shows me this:

error: no matching function for call to
‘for_each(__gnu_cxx::__normal_iterator<std::tr1::shared_ptr<yarl::item::Item>*,
std::vector<std::tr1::shared_ptr<yarl::item::Item>,  
std::allocator<std::tr1::shared_ptr<yarl::item::Item> > > >,  
__gnu_cxx::__normal_iterator<std::tr1::shared_ptr<yarl::item::Item>*,  
std::vector<std::tr1::shared_ptr<yarl::item::Item>,   
std::allocator<std::tr1::shared_ptr<yarl::item::Item> > > >, <unresolved overloaded
function type>)’  
/usr/include/c++/4.4/bits/stl_algo.h:4194: note: candidates are: _Funct  
std::for_each(_IIter, _IIter, _Funct) [with _IIter =  
__gnu_cxx::__normal_iterator<std::tr1::shared_ptr<yarl::item::Item>*,   
std::vector<std::tr1::shared_ptr<yarl::item::Item>,   
std::allocator<std::tr1::shared_ptr<yarl::item::Item> > > >, _Funct = void 
(yarl::party::Party::*)(yarl::itemPtr)]

Hard to read, to say the least. I imagine the solution is pretty simple, but I can't figure out what g++ is complaining about. The signature of investigators.addToLeaderInventory() is this:

void ClassName::addToLeaderInventory(itemPtr item);

which should work with a for_each, shouldn't it? What should I change?

like image 539
Max Avatar asked Jul 11 '10 00:07

Max


3 Answers

C++ can't bind an object and a method together into a single callable "function". You have to do the binding explicitly, either via a object with a custom operator()...

class AddToLeaderInventory {
public:
    AddToLeaderInventory(party::Party& party) : party_(party) { }

    void operator()(item::Item& i) { party_.addToLeaderInventory(i); }

private:
    party::Party& party_;
};
...
std::for_each(items.begin(), items.end(), AddToLeaderInventory(investigators));

...or using a library such as Boost.Bind.

like image 33
Marcelo Cantos Avatar answered Oct 31 '22 17:10

Marcelo Cantos


for_each takes a callable entity of some kind. In order to call a member function on another object, you need to use mem_fun, which wraps the member function so that it can be called like an ordinary function, then you need to bind it to the object instance on which it should be called using bind1st:

std::for_each(items.begin(), items.end(), 
    std::bind1st(std::mem_fun(&ClassName::add), &investigators));

Another option is to use the more modern bind, which your implementation may provide in the std or std::tr1 namespace (if it doesn't, you can use the implementation from Boost):

using std::placeholders::_1;

std::for_each(items.begin(), items.end(), 
    std::bind(&ClassName::add, &investigators, _1);
like image 129
James McNellis Avatar answered Oct 31 '22 15:10

James McNellis


If you have lambdas then you could do

for_each(items.begin(), items.end(), 
         [&](const ItemPtr& it) {investigators.addToLeaderInventory(it);});
like image 1
obelix Avatar answered Oct 31 '22 15:10

obelix