A few days ago, I saw Jonathan Boccara’s post on std::for_each . While I haven’t decided yet on whether I agree with everything said there, he makes a really nice point about the levels of abstraction and that even if they look similar enough, the std::for_each algorithm and the range-based for loop are not really meant for the same thing. And I couldn’t agree more.

One of the slides that I often had in my talks is exactly about this distinction. I used to ask “What is the difference between these two?”:

for (item: items) { // do something } for_each(items, [] (item) { // do something });

The answers I usually got at first were about how each of these handle references, and about the for_each algorithm being able to process just a part of a collection, or to process the elements in a reversed order since it receives a pair of iterators and we can pass in the reverse iterators rbegin and rend . Or that it can use const iterators instead of the normal ones. And more along those lines.

These answers are all correct, but all of these changes can be bypassed one way or the other.

THE ABSTRACT ___ _ / __) | | _| |__ ___ ____ _____ _____ ____| |__ (_ __) _ \ / ___) | ___ (____ |/ ___) _ \ | | | |_| | |_______| ____/ ___ ( (___| | | | |_| \___/|_(_______)_____)_____|\____)_| |_| CHRONICLES

Abstractions

Now, this slide intentionally contained pseudo-code – no type declaration for the item variable in the range-based for loop nor for the lambda argument and a for_each receiving a collection (like in boost ) instead of an iterator pair. The idea was to try to make people not to focus on these things but on the bigger picture.

While the range-based for loop is quite useful, it is nothing more than an ordinary for loop that operates on iterators. Every time we write a range-based for loop, the compiler sees this in C++17:

{ auto && __range = range_expression ; auto __begin = begin_expr ; auto __end = end_expr ; for ( ; __begin != __end; ++__begin) { range_declaration = *__begin; loop_statement } }

Whatever you do, you can not change this (unless, of course, you write a proposal for changing the C++ standard, it gets voted in, and the compiler vendors implement this change).

But, what happens when we call for_each ?

for_each(begin, end, function);

There is one thing intentionally missing from this code snippet – we are calling for_each without specifying its namespace. If begin and end are iterators to a collection from the standard library, the above code will call the std::for_each algorithm thanks to the ADL - argument-dependent lookup (formerly known as Koenig lookup).

ADL specifies that, when looking up an unqualified function name, the compiler should try to find that function in the namespace of its arguments’ types in addition to the normal name lookup. In the case where begin and end are iterators to a std::vector<T> , the iterators live in the std:: namespace, and the compiler will find the std::for_each algorithm.

What is for_each?

This all means that, unlike the range-based for loop which has a fixed semantics to always iterate over a given sequence and execute the body of the loop for each element in that sequence, the call to for_each can mean absolutely anything. It depends on the type passed to it.

You can be anything you want to be.

Just turn yourself into anything

you think you could ever be. ~ Innuendo by Queen

Imagine the following – we want to create a UI for drawing graphs, and we want to make it create a new graph node every time the user clicks somewhere on the canvas component.

We want to create a new graph node for each mouse click.

Mouse clicks are not stored in a vector or a similar collection, they just appear one by one asynchronously when the user clicks the mouse. We can not use iterators to go through all the clicks and therefore we can not use the range-based for loop to iterate trough them and create graph nodes. It is a some kind of sequence, but it is an asynchronous sequence and is not iterable.

But again, we want to create a new graph node for each mouse click. And the code should be able to express that.

Consider the following code snippet:

for_each(begin(canvas.click_events), end(canvas.click_events), [] (point2d coordinate) { // create a new graph node } );

Looking at it, it is obvious what it does – for each mouse click event that happens on the canvas object, it creates a new graph node.

In the same way a plus operator can mean different things depending on the type it operates on – number addition, string concatenation, set union, etc. – for_each can mean different things for different types. It should always be used to define a function to be executed for each element, like the plus operator should always do some sort of addition, but the meaning of what for each element means for a given type can vary significantly.

In the mouse click example, it would mean “register this lambda as the mouse click event handler” (or “connect the clicked signal to the lambda” if you’re into Qt).

This idea can work on other types as well – for a std::optional<T> , it would mean “execute on the value stored inside the optional, if the value exists”; for a std::future<T> , it would mean “when the future value arrives, execute the given function on it”; and so on.

What’s the point

Now, you might be wondering what would be the point of using for_each for these things. Why not just use the regular event handler registration mechanisms to listen to mouse clicks, use the .then on the future values, etc.?

Just like iterators are abstractions over pointers that allow us to write generic code which works on any sane collection, for_each is a common abstraction that can work on anything for each has a meaning for. It is generic programming and polymorphism at its finest.

One of the immediate benefits of using abstractions like this in your code is that it becomes much more easy to test.

Do you want to test whether the program is creating the graph nodes in the right places? Just run the same code that uses for_each to listen to mouse clicks, but replace the event stream with a pre-populated std::vector<point2d> containing any coordinates you like. No need for mocking and no need for complex testing systems or anything.

How to implement?

The for_each can easily be implemented for various types. We just need to implement the begin and end functions for our types to have a return type that lives in a custom namespace (this return type does not need to have anything in common with iterators if the types we are implementing begin and end functions for are not iterable), and implement the for_each function for these types in that custom namespace.