开发者

Can a functor retain values when passed to std::for_each?

According to the first answer to this question, the functor below should be able to retain a value after being passed to foreach ( I couldn't get the struct Accumulator in the example to compile, so built a class).

class Accumulator
{
    public:
        Accumulator(): counter(0){}
        int counter;
        void operator()(const Card & c) { counter += i; }
};

Example usage ( as per the example )

// Using a functor
Accumulator acc;
std::for_each(_cards.begin(), _cards.end(), acc);
// according to the example - acc.counter contains the sum of all
// elements of the deque 

std::cout << acc.counter << s开发者_开发知识库td::endl;

_cards is implemented as a std::deque<Card>. No matter how long _cards gets, acc.counter is zero after the for_each completes. As I step through in the debugger I can see counter incrementing, however, so is it something to do with acc being passed by value?


This was just asked here.

The reason is that (as you guessed) std::for_each copies its functor, and calls on it. However, it also returns it, so as outlined in the answer linked to above, use the return value for for_each.

That said, you just need to use std::accumulate:

int counter = std::accumulate(_cards.begin(), _cards.end(), 0);

A functor and for_each isn't correct here.


For your usage (counting some, ignoring others), you'll probably need to supply your own functor and use count_if:

// unary_function lives in <functional>
struct is_face_up : std::unary_function<const Card&, const bool>
{
    const bool operator()(const card& pC) const
    {
        return pC.isFaceUp(); // obviously I'm guessing
    }
};

int faceUp = std::count_if(_cards.begin(), _cards.end(), is_face_up());
int faceDown = 52 - faceUp;

And with C++0x lambda's for fun (just because):

int faceUp = std::count_if(_cards.begin(), _cards.end(),
                            [](const Card& pC){ return pC.isFaceUp(); });

Much nicer.


Yes, it's definitely linked to acc being passed by value.

Modify your accumulator as follows :

class Accumulator
{
    public:
        Accumulator(): counter(new int(0)){}
        boost::shared_ptr<int> counter;
        void operator()(int i) { *counter += i; }

        int value() { return *counter; }
};


This is because internally the std::for_each() makes a copy of the functor (as it is poassable to pass temporary object). So internally it does do the sum on the copy not on the object you provided.

The good news is that std::for_each() returns a copy of the functor as a result so you can access it from there.

Note: There are other standard algorithms you could use. Like std::accumulate().
But suppose this is just a simplified example and you need for_each() to something slightly tricker than the example there are a couple of techniques to allow you access to the accumulator object.

#include <iostream>
#include <algorithm>
#include <vector>

class Card{ public: int i;};
class Accumulator
{
    public:
        Accumulator(): counter(0){}
        int counter;
        void operator()(const Card & c) { counter += c.i; }
};


int main()
{
    std::vector<Card>   cards;

    Accumulator a = std::for_each(cards.begin(), cards.end(), Accumulator());

    std::cout << a.counter << std::endl;

}

Alternatively you can change you Accumalator to increment a reference that is used within the current scope.

#include <iostream>
#include <algorithm>
#include <vector>

class Card{ public: int i;};
class Accumulator
{
        int&  counter;
    public:
        // Pass a reference to constructor.
        // Copy construction will pass this correctly into the internal object used by for_each
        Accumulator(int& counterRef): counter(counterRef){}
        void operator()(const Card & c) { counter += c.i; }
};


int main()
{
    std::vector<Card>   cards;

    int counter = 0;  // Count stored here.

    std::for_each(cards.begin(), cards.end(), Accumulator(counter));

    std::cout << counter << std::endl;

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜