Click here to Skip to main content
Email Password   helpLost your password?

Introduction

How many times have you read about the merits of using the STL algorithms instead of hand written loops? Chances are this is something that you have heard a lot. The thing is, actually being able to achieve this in real world programs is not very intuitive; especially if you are new to the STL. However, once you learn how the STL algorithms interact with your code, using for_each becomes easy. In fact, it becomes so easy and it is so powerful, that you may never write another loop in your career.

The for_each algorithm

The for_each algorithm iterates over a container and �does something� for each element in the container. As a programmer, you have to specify what you want to be done for each element when you call the for_each algorithm. You do this by providing a function which the algorithm calls as it iterates over the container.

The algorithm is not a magic. It does what you think it does. So let�s take a look at the implementation of for_each that comes with Microsoft�s C++ 7:

/*
 * Copyright (c) 1992-2002 by P.J. Plauger.  ALL RIGHTS RESERVED.
 * Consult your license regarding permissions and restrictions.
 */
// TEMPLATE FUNCTION for_each

template<class _InIt, class _Fn1> inline
    _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
    {  
       // perform function for each element

       for (; _First != _Last; ++_First)
           _Func(*_First);
       return (_Func);
    }

As you can see, the algorithm iterates over the range passed in and invokes a function for each element in the range.

The general idea while replacing loops with for_each is that you write a function implementing the loop logic, that is, what you want to do for each iteration of the loop. You then supply this function as the third parameter (_Func) to the for_each algorithm. Sounds good? Well, I�m afraid that there�s a little more to it than that.

Notice that the signature of the function called by for_each is fixed. It has to be a function taking just one parameter and this parameter has to be of the same type as the elements in the container. So if you write a function implementing your loop logic, the only parameter it can have is the current element in the loop; and herein lays the problem.

What if you want to have more that one parameter?

Use function objects to build logic hierarchies

Let�s take a specific example. Say that we are looping over a vector <wstring> and we want to compare each <wstring> in the vector to an external wstring object and write it out to a file if they are the same.

Without using for_each, our code would look something like the following:

wstring wstrTarget;
wstring wstrFile;
for ( vector<wstring>::iterator itr = vec.begin(); itr != vec.end(); itr++ )
{
    if ( *itr == wstrTarget )
        writeToFile( *itr, wstrFile );
}

How would we do the same thing using for_each? Well, we need a function that can compare the current element to wstrTarget and, if they match, write the current element out to the file called wstrFile. So, the function needs to know three things: the current element, what to match it to and the name of the file to write to. But how do we tell the function these three things? Remember that we can only supply the function with one parameter, the current element, so how do we get the other pieces of information into the function?

The answer lies in the function objects.

Because function objects can be called like functions, we can use one as the function passed into for_each. That is, in the for_each implementation given above, _Func(*_First) can call a function object as well as a function. Also, because function objects are objects (like any other) they can have properties, methods and constructors. This allows us to supply the function object with the additional data it needs to perform the loop logic which cannot be supplied automatically by the for_each algorithm.

A convenient and concise way of doing this is by supplying the extra data in the constructor of the function object.

In our example, we can define a function object called compareAndWrite which accepts the target wstring object and the file name to write to in its constructor. Defining an operator() accepting a const wstring& as a parameter allows the function object to be used by the for_each algorithm and receive the current element from each iteration.

Putting all the pieces together, we get the following definition for the compareAndWrite function object:

struct compareAndWrite 
{
    const wstring& wstrTarget;
    const wstring& wstrFile;

    compareAndWrite( 
        const wstring& wstrT, 
        const wstring& wstrF 
        ) : wstrTarget(wstrT), wstrFile(wstrF)
    {
    }

    void operator()( const wstring& val ) 
    {
        if ( val == wstrTarget )
            writeToFile( val, wstrFile );
    }

};

which allows us to replace the hand crafted for loop with the following call to for_each.

for_each( vec.begin(), vec.end(), compareAndWrite(wstrTarget,wstrFile) );

Why is this better?

OK, so we have to write more code to use function objects, but the extra code is worth writing and here�s why.

The extra code is required to define the function object. We have to define the class structure, methods and variables. Once we have the class defined, the actual loop logic is really not too different at all and is defined by the class�s operator().

So why bother using the for_each algorithm when more code is required?

Well, with hand crafted loops, how do you:

Reusing a loop without function objects invariably involves making a new copy of the loop code. This is not good. You now have two copies of the same code in two different parts of your software.

Building a slightly different version of the loop will also normally involve taking a copy of the code and modifying the loop logic. Again, you now have two chunks of code doing similar things that have to be maintained.

Function objects and the for_each algorithm give you the ability to build your loop logic as a fully fledged class and reap all the benefits that come from inheritance, polymorphism and encapsulation.

You can build a class hierarchy defining the different versions of your loop logic without having to maintain multiple copies of the same code or complicate the loop logic with parameterization (as would be the case if you defined the loop logic in a function rather than a function object).

So, in this example, to use a slightly different version of the compareAndWrite loop, all we need to do is define a subclass of the compareAndWrite function object and use that in the call to for_each.

struct multiCompareAndWrite :
    public compareAndWrite
{
    // loop logic here

};
for_each(vec.begin(), vec.end(), 
     multiCompareAndWrite(wstrTarget,wstrFile));

Other benefits

Summary

Replacing for loops with the STL for_each algorithm is a great way of making object oriented programming work for you. You can avoid having multiple copies of the same or similar code to maintain by building class hierarchies of loop logic. What�s more, your code becomes less cluttered, easier to read and more componentized.

The key aspects of this technique are:

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Questioncommunication
gagaro
19:53 24 Sep '06  
is there any communication project that links up 2 computers

GeneralYou Drank the Kool Aid
drudru
20:34 1 Jun '05  

I've tried this in the past, but the reality is that having
to build functors for simple 'for' loops is bad practice.
This is definitely one area where the STL doesn't work so well.
(Another area is reverse iterators)
GeneralRe: You Drank the Kool Aid
Frast
7:20 2 Jun '05  
I fully agree with that. Reverse iterators are an good idea, but in practise I often need to compare reverse iterators to normal iterators. This is not possible in the stl. An iterator is a pointer to a contained item right. Why not compare one pointer to another? Confused
GeneralRe: You Drank the Kool Aid
Don Clugston
17:24 5 Jun '05  
Frast wrote: I fully agree with that. Reverse iterators are an good idea, but in practise I often need to compare reverse iterators to normal iterators. This is not possible in the stl. An iterator is a pointer to a contained item right. Why not compare one pointer to another?
Use .base() to convert from forward to reverse iterator. They aren't quite the same as pointers: a reverse iterator typically points to the element BEFORE the one that the equivalent forward iterator points to.
Effective STL item 28 has a nice diagram and explanation.


GeneralRe: You Drank the Kool Aid
CP Visitor
3:02 7 Jun '05  
Don Clugston wrote: Use .base() to convert from forward to reverse iterator.
from reverse to forward Wink

They aren't quite the same as pointers: a reverse iterator typically points to the element BEFORE the one that the equivalent forward iterator points to.
Effective STL item 28 has a nice diagram and explanation.

see also: Guideline 3:[^]

GeneralRe: You Drank the Kool Aid
Don Clugston
17:25 5 Jun '05  
Frast wrote: I fully agree with that. Reverse iterators are an good idea, but in practise I often need to compare reverse iterators to normal iterators. This is not possible in the stl. An iterator is a pointer to a contained item right. Why not compare one pointer to another?
Use .base() to convert from forward to reverse iterator. They aren't quite the same as pointers: a reverse iterator typically points to the element BEFORE the one that the equivalent forward iterator points to.
Effective STL item 28 has a nice diagram and explanation.

GeneralRe: You Drank the Kool Aid
Zac Howland
7:53 18 May '06  
drudru wrote:
I've tried this in the past, but the reality is that having
to build functors for simple 'for' loops is bad practice.

You do realize that you don't have to create functors for simple loops right? The following code is perfectly fine on almost all modern compilers:

void print(int i)
{
cout << i << endl;
}

void main()
{
int myArray[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for_each(myArray, myArray + 10, print);
}


If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
GeneralUnfortunately, that's not all
Don Clugston
17:22 29 May '05  
There's another downside to for_each which you didn't mention: the code in the functor is physically seperated from the loop.
I find that is quite rare for my functors to be reused. So all for_each does for me is obfuscate the code by moving the loop action to a functor in a different function.
for_each is a great idea, but it is not going to work well until C++ allows class definitions inside functions. Anonymous functions would be ideal.
With the more complex stl algorithms, using a functor is definitely worthwhile, but IMHO for_each doesn't add enough value to pay the obfuscation cost. Similarly for std::accumulate()in most cases.

Good explanation, though.
GeneralRe: Unfortunately, that's not all
Roland Pibinger
0:16 30 May '05  
Don Clugston wrote: There's another downside to for_each which you didn't mention: the code in the functor is physically seperated from the loop. I find that is quite rare for my functors to be reused. So all for_each does for me is obfuscate the code by moving the loop action to a functor in a different function.
Agreed. The for loop is the most generic 'for_each' ever invented.;)

for_each is a great idea, but it is not going to work well until C++ allows class definitions inside functions. Anonymous functions would be ideal.
You probably mean until C++ allows class definitions inside functions be used as template arguments. But defining (non-reusable) code inside a function makes the use of for_each even more questionable.

With the more complex stl algorithms, using a functor is definitely worthwhile, but IMHO for_each doesn't add enough value to pay the obfuscation cost. Similarly for std::accumulate()in most cases.
I completely agree with you!

Good explanation, though.
Apart from the fact that for_each and STL have nothing to do with "making object oriented programming work for you". Actually, Mr. Stepanov, inventor of STL, abhors OO programming.





GeneralRe: Unfortunately, that's not all
Martin Friedrich
21:59 1 Jun '05  
Yet another pitfall becomes obvious, if functors need to access elements in the target collection based on the iteratos current position. For example, things like a[ i ] = a[ 2 * i ]. This can become quite tricky to do in a functor and definitely WILL clutter your code.


Roland Pibinger wrote: Apart from the fact that for_each and STL have nothing to do with "making object oriented programming work for you". Actually, Mr. Stepanov, inventor of STL, abhors OO programming.
Actually, STL is all about generic programming. People tend to mix up this with OOP, unfortunately. Maybe it's because of OOP's buzzword potential Smile

Bye,
Martin Friedrich

GeneralRe: Unfortunately, that's not all
Gabhan Berry
0:46 2 Jun '05  
The reference to OOP is in regards to being able to build hierarchies of functors that implement general and specialised cases of the loop logic; what I refer to as logic hierarchies. My point was that combining class hierarchies (using OOP) with the for_each algorithm is a practical example of utilising OOP and the STL together; I did not mean to imply that the STL is all about OOP.

Perhaps I should clarify this in an update to the article ...

In the case of having to access the elements in the container based on the current element's position, this can be done by having a member variable in the functor that acts as an index. You increment this index each time operator() is invoked.

i.e. something like:

struct loopLogic{
size_t index;
loopLogic() : index(0){
}

void operator()( const your_container::value_type& val ){
// loop logic here
// distance from beginning of range is the value of index
++index; // increment the index at the end of the method
}
};

If the start of the range is not the start of the container, then the difference of these two points (i.e the initial offset) can be supplied in the functor's constructor (if required by the loop logic). Likewise, if you need to know the distance from the end of the range, you can pass the size of the range into the functor's constructor and then (size-index) gives you the distance from the end.

I don't think that this is too messy..

Thanks for your feedback.



GeneralRe: Unfortunately, that's not all
Tanveer Badar
7:50 1 Jun '05  
There is another downfall in using for_each for newbies.They see it as the only solution to their problems and use it even in cases where for_each is explicitly prohibited.There are requirements on the functor that it cannot have any side effects.
GeneralRe: Unfortunately, that's not all
Zac Howland
7:56 18 May '06  
Tanveer Badar wrote:
There is another downfall in using for_each for newbies.They see it as the only solution to their problems and use it even in cases where for_each is explicitly prohibited.There are requirements on the functor that it cannot have any side effects.

That is when other algorithms (e.g. transform) are better.


If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
GeneralRe: Unfortunately, that's not all
Gabhan Berry
0:32 2 Jun '05  
If you are using your functor only once, then I can see how having the functor definition close to where it is being used is useful and class definitions inside functions would be a great way of solving the proximity issue.

If you need to call your functor from multiple places then the proximity issue is not so much of a problem, I feel.

Thanks for the feedback... I like your ideas.
AnswerRe: Unfortunately, that's not all
~louis
6:23 17 Feb '07  
MS VC8 allows you to define a class inside a function and use it as an argument to for_each (or accumulate, etc) on the next line in the same function... so I believe that matter is solved, at least for VC 2005.
GeneralRe: Unfortunately, that's not all
LBRODY2
19:57 4 Jun '05  
I agree that it would be easier and cleaner to use for_each with anonymous functions, take a look at the boost lambda library (boost.org), it supplies just that, and still gives you easy to read code for simple function objects.

Leslie


Last Updated 28 May 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010