89

Both can be used to apply a function to a range of elements.

On a high level:

  • std::for_each ignores the return value of the function, and guarantees order of execution.
  • std::transform assigns the return value to the iterator, and does not guarantee the order of execution.

When do you prefer using the one versus the other? Are there any subtle caveats?

Null
  • 1,950
  • 9
  • 30
  • 33
bendervader
  • 2,619
  • 2
  • 19
  • 22
  • 9
    `transform` has an output range, `for_each` doesn't. – user657267 Jun 26 '15 at 04:14
  • 5
    A lot of `` consists of functions that are very similar, just have a *slight* difference. – o11c Jun 26 '15 at 04:15
  • 2
    @o11c this is exactly what I'm looking for. There must be a good reason for the slight difference. – bendervader Jun 26 '15 at 05:46
  • 2
    @bendervader The point is: `` is used a *lot*. Convenience is the biggest (if not only) motivating factor for that whole header. – o11c Jun 26 '15 at 05:53
  • Lots of algorithms that take a Callable have to work around `void` return values, have you tried assigning `void` to *anything*? – Caleth Jan 08 '18 at 17:42

4 Answers4

79

std::transform is the same as map. The idea is to apply a function to each element in between the two iterators and obtain a different container composed of elements resulting from the application of such a function. You may want to use it for, e.g., projecting an object's data member into a new container. In the following, std::transform is used to transform a container of std::strings in a container of std::size_ts.

std::vector<std::string> names = {"hi", "test", "foo"};
std::vector<std::size_t> name_sizes;

std::transform(names.begin(), names.end(), std::back_inserter(name_sizes), [](const std::string& name) { return name.size();});

On the other hand, you execute std::for_each for the sole side effects. In other words, std::for_each closely resembles a plain range-based for loop.

Back to the string example:

std::for_each(name_sizes.begin(), name_sizes.end(), [](std::size_t name_size) {
    std::cout << name_size << std::endl;
});

Indeed, starting from C++11 the same can be achieved with a terser notation using range-based for loops:

for (std::size_t name_size: name_sizes) {
    std::cout << name_size << std::endl;
}
madmech
  • 46
  • 5
Ilio Catallo
  • 3,152
  • 2
  • 22
  • 40
  • 5
    to extend on this, in C++14 we can remove the type from the range based for loop to now be: for (name_size: name_sizes){ ... } – Trevor Hickey Jul 08 '15 at 05:55
  • nice! last time I checked it was just a proposal. I generally use `for (auto&& x: xs)`, but that level of abstraction was not really crucial for the explanation. – Ilio Catallo Jul 08 '15 at 06:56
  • 2
    @TrevorHickey I quickly checked again, and I cannot find any news apart the original proposal N3853. Are you sure about this? – Ilio Catallo Jul 08 '15 at 21:28
  • 1
    I'm mistaken. This is not a part of C++14, but it will be a part of C++17. You can run it on Clang/GCC now given the right flag: http://coliru.stacked-crooked.com/a/60d36ec0325242b7 – Trevor Hickey Jul 08 '15 at 21:56
  • 1
    Not exactly. `std::for_each` is not meant to have side effects, i.e. modifying the container by reference, as another user pointed out. It's not consistent with the principles, as the same user pointed out. – Jossie Calderon Jun 27 '16 at 11:52
  • 1
    I don't agree with your definition of side effect (the "i.e." part). Printing out any information to the console is a typical example (if not "the" example) of side effect. This is in fact coherent to @bugshotgg's explanation you are probably mentioning. – Ilio Catallo Jun 27 '16 at 13:45
25

Your high level overview

  • std::for_each ignores the return value of the function and guarantees order of execution.
  • std::transform assigns the return value to the iterator, and does not guarantee the order of execution.

pretty much covers it.

Another way of looking at it (to prefer one over the other);

  • Do the results (the return value) of the operation matter?
  • Is the operation on each element a member method with no return value?
  • Are there two input ranges?

One more thing to bear in mind (subtle caveat) is the change in the requirements of the operations of std::transform before and after C++11 (from en.cppreference.com);

  • Before C++11, they were required to "not have any side effects",
  • After C++11, this changed to "must not invalidate any iterators, including the end iterators, or modify any elements of the ranges involved"

Basically these were to allow the undetermined order of execution.

When do I use one over the other?

If I want to manipulate each element in a range, then I use for_each. If I have to calculate something from each element, then I would use transform. When using the for_each and transform, I normally pair them with a lambda.

That said, I find my current usage of the traditional for_each being diminished somewhat since the advent of the range based for loops and lambdas in C++11 (for (element : range)). I find its syntax and implementation very natural (but your mileage here will vary) and a more intuitive fit for some use cases.

Niall
  • 30,036
  • 10
  • 99
  • 142
  • 1
    could you pls elaborate a bit on why "std::transform ... does not guarantee the order of execution"? – athos Jun 04 '17 at 14:27
  • 2
    This is in contrast to the `for_each` algorithm, it is required to apply the function to each element in order. The requirements of the `transform` operations to not have any side affects means the order of execution wouldn't be observable, so not guaranteed. That all said, the implementations I've looked at all apply it in the expected order, first to last. – Niall Jun 04 '17 at 18:54
18

Although the question has been answered, I believe that this example would clarify the difference further.

for_each belongs to non-modifying STL operations, meaning that these operations do not change elements of the collection or the collection itself. Therefore, the value returned by for_each is always ignored and is not assigned to a collection element. Nonetheless, it is still possible to modify elements of collection, for example when an element is passed to the f function using reference. One should avoid such behavior as it is not consistent with STL principles.

In contrast, transform function belongs to modifying STL operations and applies given predicates (unary_op or binary_op) to elements of the collection or collections and store results in another collection.

#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

void printer(int i) {
        cout << i << ", ";
}
int main() {
    int mynumbers[] = { 1, 2, 3, 4 };
    vector<int> v(mynumbers, mynumbers + 4);

    for_each(v.begin(), v.end(), negate<int>());//no effect as returned value of UnaryFunction negate() is ignored.
    for_each(v.begin(), v.end(), printer);      //guarantees order

    cout << endl;

    transform(v.begin(), v.end(), v.begin(), negate<int>());//negates elements correctly
    for_each(v.begin(), v.end(), printer);
    return 0;
}

which will print:

1, 2, 3, 4, 
-1, -2, -3, -4, 
BugShotGG
  • 5,008
  • 8
  • 47
  • 63
  • 2
    Your emphasizing on the `modifiability` aspect is hocus-pocus. Because you admit that `for_each` can be made to modify something, although not idiomatic if doing so. In conclusion, both `for_each` and `transform` can indeed `modify` according to your own statements. And therefore, `modifiability` is not a determining characteristic to differentiate them. Rather than emphasizing `modifiability`, I would rather emphasize `order` because this is an absolute differentiator based on specification. – daparic May 07 '21 at 17:00
  • 3
    @eigenfield How is the aspect hocus-pocus. It is how it is defined and how it was intended to be used. Finding a workaround doesnt change this. – BugShotGG May 16 '21 at 20:50
2

Real example of using std::tranform is when you want to convert a string to uppercase, you can write code like this :

std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);

if you will try to achieve same thing with std::for_each like :

std::for_each(s.begin(), s.end(), ::toupper);

It wont convert it into uppercase string

mystic_coder
  • 462
  • 2
  • 10