tags:

views:

114

answers:

1

I've recently started teaching myself the standard template library. I was curious as to why the GetTotal() method in this class is returning 0?

...

class Count
{
public:
    Count() : total(0){}
    void operator() (int val){ total += val;}
    int GetTotal() { return total;}
private:
    int total;
};

void main()
{
    set<int> s;
    Count c;
    for(int i = 0; i < 10; i++) s.insert(i);
    for_each(s.begin(), s.end(), c);
    cout << c.GetTotal() << endl;
}
+13  A: 

for_each takes the function by-value. That is, it uses a copy of the functor and not the functor itself. Your local c is left unchanged.

for_each returns the functor it used, though, so you could do:

Count c;
c = for_each(s.begin(), s.end(), c);

Or more idiomatically:

Count c = for_each(s.begin(), s.end(), Count());

However, there exists such functionality already (no need for your functor):

int total = std::accumulate(s.begin(), s.end(), 0);
GMan
That's counter intuitive. Do you know why they did it that way?
Mark Ransom
It is because c is on the stack and is passed by value to for_each, so a copy of c is passed. It has nothing to do with for_each (other then it takes a functor instead of a pointer to a functor), but instead with the semantics of the language.As to why it takes a functor and not a pointer to a functor could be related to it also being able to take a pointer to a function, so that no matter what is passed in, for_each can call f(). But that part is just a guess
Brandon Bodnár
@Mark: I don't know actually. They could have made it a reference and there shouldn't be any problems. I can't think of a situation where it being a reference would surprise me either. I'm curious as well. @Brandon: He's not asking what my answer means. He means "why didn't they design it to take the function by-reference instead of by-value?" :)
GMan
@Mark mostly for backward compatibility with (the original) C, in which parameters were taken only by value. So if you pass a free function to an algorithm you can expect the template parameter to be a plain C function pointer taken by value. As a side-note, for this same reason iterators are taken by value too everywhere in the STL.
wilhelmtell
@wilhelmtell: But what's the harm it taking it by-reference? It wouldn't effect free-functions in any way.
GMan
@GMan, if it is for backward compatibility, then they won't allow pass by-reference, since you can't pass by reference in C. You can only pass by value.
Brandon Bodnár
@Brandon: Why would this need to be backwards compatibly with C? This is C++. I mean the entire thing is templated, there's no C to be compatible with.
GMan
I don't know why it would be compatible with C, hence why I prefaced my comment with **if**. whilhelmtell is who said it was for backward compatibility with C.
Brandon Bodnár
@Brandon: Oh I see. Yeah, there's nothing to be backwards compatible with here. If I were to design the STL from the ground up, the function would be by-reference. Time to search around for an answer. :)
GMan
@GMan: Yeah, pass by reference would make the most since, and I don't think it would have caused any problems. Maybe I'll try later to re implement for_each and see if it can be done with pass by reference with no major errors.
Brandon Bodnár
If they made it a modifiable reference, that would exclude passing a temporary (which is the most common usage). If they made it a const reference, that would go back to square 1 regarding stateful functors like this one… it would have to make a copy and return by value. Determining that the const reference aliases the argument would likely be harder for the inlining engine than double copy elision, which is how things work. I think the specification was very forward-thinking.
Potatoswatter
GMan
wow, I can believe I missed that. Thanks!
Jon
@Potatoswatter Yes! Taking by value allows you to pass temporaries and const objects. The standard gives algorithms the freedom to make internal copies of the functors too, so that defeats the point of passing by reference. If you really want to use a stateful functor and access its state after the call then you have two options: either use `for_each()` and take the return value, or instantiate the algorithm's template to take a reference for the functor. Predicates, btw, should always be stateless.
wilhelmtell