Is std::for_each obsolete?
I’m often asked the question: with C++11 introducing range-based for loops, is std::for_each
now useless?
And the short answer is: No.
Let’s give a quick recap on for_each
and range-based for loops, and then a heuristic for choosing between the two.
for_each
and range-for loops
for_each
std::for_each
is an STL algorithm that takes a collection of elements (in the form of a begin and end iterator) and a function (or function object), and applies the function on each element of the collection. It has been there since C++98.
To say this codewise, the following piece of code:
std::vector<int> numbers = {1, 2, 3, 4, 5}; std::for_each(begin(numbers), end(numbers), f);
applies the function f
on each element of numbers
.
range-based for loops
Range-based for loops are a native language construct present in many languages, and were added to C++11:
std::vector<int> numbers = {1, 2, 3, 4, 5}; for (auto number : numbers) { // do something with number }
Note that in this example I used auto
which makes a copy of each element, but I could also have used auto&
to take a non-const reference or auto const&
for a constant reference. Or directly use int
instead of auto
, though auto
seems more convenient to me here.
Similar but different
Sometimes std::for_each
is seen as a complicated way to express the same thing as range-based for loops. Consider the following piece of code, using a lambda:
std::for_each(begin(numbers), end(numbers), [](int number) { // do something with number });
It looks very much like a range-based for loop, but with a mouthful of syntax added on the top. It’s because this is the wrong way to use std::for_each
in C++11.
for_each
is a tool for raising the level of abstraction of a range-based for loop. And showing the inside of the lambda within the call to for_each
kills this purpose.
Let’s illustrate this with an example. Let’s write a piece of code that displays the numbers of the collection with a particular format, say for giving instructions to a calculator:
- if the number is not zero it outputs it, preceded by its sign(“+” or “-“),
- if the number is zero, it outputs “nop”.
Writing this with a range-based for loop gives the following code:
std::vector<int> numbers = {1, -2, 3, 0, 5}; for (auto number : numbers) { if (number > 0) std::cout << '+' << number << '\n'; else if (number == 0) std::cout << "nop" << '\n'; else if (number < 0) std::cout << number << '\n'; }
outputting:
+1 -2 +3 nop +5
Now this code draws its reader into too many details, particularly if it is in the middle of a larger function. One simple way to factor out the logic of display is to encapsulate it into a function:
void displayAsInstruction(int number) { if (number > 0) std::cout << '+' << number << '\n'; else if (number == 0) std::cout << "nop" << '\n'; else if (number < 0) std::cout << number << '\n'; }
And replace the loop code with this function:
for (auto number : numbers) { displayAsInstruction(number); }
It’s much better, but the number
variable has no use any more. std::for_each
packs it off:
std::for_each(begin(numbers), end(numbers), displayAsInstruction);
Making the most out of for_each
The number
variable is gone, but a lot has appeared: the begin and end iterators, which we don’t need here (there are cases where they are useful though, like when applying a function until a certain element of the collection. But here they are just noise).
We were in the process of raising the level of abstraction by hiding the implementation of the display, and we’re back with new implementation details: iterators.
Here is how to fix this: wrap std::for_each
to give it range semantics. Add this in a utilities header:
#include <algorithm> namespace ranges { template<typename Range, typename Function> Function for_each(Range& range, Function f) { return std::for_each(begin(range), end(range), f); } }
And you can use it this way:
ranges::for_each(numbers, displayAsInstruction);
which reads what it does and with no extra information. Well, you could argue that the namespace should be taken away in a local using directive, but I guess this is a matter of taste.
It all comes down to levels of abstraction
Range-based for loops and for_each
are two tools that serve different purposes.
Range-based for loops allow to write code directly at the loop site, but to keep expressiveness this code needs to be at the same abstraction level as the code surrounding the loop.
for_each
allows to keep the abstraction level of the calling code by pushing implementation down into a dedicated function or function object, and replacing it with the function name as a label. To really achieve its purpose for_each
needs range semantics.
As always, it all comes down to respecting levels of abstraction.
Related articles:
Don't want to miss out ? Follow:   Share this post!