[Dev] STL for_each underwhelms me

M. Mueller (bhu5nji) dev@trilug.org
Mon, 11 Feb 2002 16:07:02 -0500


>
> Perhaps it would help if you would explain a bit more about
> what you're trying to do, and why a unary function won't work
> for you?  Without knowing that, however, you probably want to
> look around for documentation about a "function object" which
> is an object designed to look like a function but that can
> hold state.

Nice description.  That's one light.  The David Musser STL Tutorial only 
talks about functions, never about function objects that have state.  So, now 
I can envision that the function object is "sent in" to the for_each 
algorithm.   


>
> Say, for example, you want to take a vector of ints and calculate
> it's mean value.  With a plain old for, you could loop through
> each element of the vector and calculate the sum and finally
> divide by the number of elements.  With for_each, however, you
> could do it like this:
>
> // function object to process the mean value
> class MeanValue {
>  private:
>     long num; // number of elements
>     long sum; // sum of all elements
>  public:
>     // Constructor
>     MeanValue() : num(0), sum(0) {}
>
>     // function call
>     void operator() (int elem) {
>       ++num; // increment count
>       sum += elem;
>     }
>
>     // return mean value
>     double value () {
>       return static_cast<double>(sum) / static_cast<double>(num);
>     }
> };
>
> int main(void)
> {
>   vector<int> coll;
>
>   // insert elements from 1 to 8
>   for (int i = 1; i <=8; ++i) {
>     coll.push_back(i);
>   }
>
>   // Process and print mean value
>   MeanValue mv = for_each(col.begin(), coll.end(), MeanValue());
>
>   cout << "mean value: " << mv.value() << endl;
>
>   return 0;
> }
>
> Note that this also shows another feature of for_each, it
> returns the (modified) function object passed to it.  As far
> as I know, this is the only STL algorithm that does that.
> I believe this is an example of what you called
> "class_with_overloaded_operators".
>
> Your "magically_transformed_things_that_appear_to_be_UF" are better
> know as "function adapters".  basically, they're functions that
> you can pass a second (or first) argument to them and they'll
> handle the fact that for_each only expects a unary function.
> So, if you wanted to multiply each element by a number, you'd
> need a function adapter that took the number you wanted to
> multiply by and provided a function for for_each to call.

This is where I cannot clearly see the benefits of using for_each.  I am 
havine the hardest time learning why I would choose to overload operators to 
create a way to squeeze in two parms where the design called for one.  I keep 
hearing the cries of those following my code, "what the heck is going on 
here?".  Why not choose (what I think is) the simpler way - write a function 
or method and call it in a for loop?  Perhaps this is just a more explicit 
way.  It is not obvious to me that using the for_each will produce better 
performance that using the for-loop.

I am saying that STL does not create simpler code.  In general, I am very 
pleased with STL.  I practically think in sets and maps these days.  I'll 
never write another linked-list.

I suspect one of my problems with for_each is a poor understanding of 
operator overloading, especially for the "function call"

>
> Also, note that if you're trying to modify a bunch of elements,
> transform is often a better function to use.  The difference
> between tranform and for_each is that for_each expects a
> unary function that returns void, so if you want to modify
> the elements, you need to pass them by reference.  Transform,
> on the other hand, takes a unary operation that returns
> a value.  This value replaces the previous value in that
> position of the container.  So, if you wanted to square a
> bunch of elements, you could do either this:
>
> void square(int &elem) { elem = elem * elem; }
> for_each(coll.begin(), coll.end() square);
>
> or this:
>
> int square(int elem) { return elem * elem; }
> transform(coll.begin(), coll.end(), // source range
>           coll.begin(),             // destination range
>           square);                  // operation.

The code here is quite readable.  


>
> But, once again, because C++ offers lots of ways to do things,
> it would help if we knew just what it was you're doing. :-)

See my reply to Brent Verner.

>
> Tanner