What is the difference between std::transform and std::for_each?

C++C++11Stl Algorithm

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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;
}

Solution 2 - C++

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.

Solution 3 - C++

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, 

Solution 4 - C++

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbendervaderView Question on Stackoverflow
Solution 1 - C++Ilio CatalloView Answer on Stackoverflow
Solution 2 - C++NiallView Answer on Stackoverflow
Solution 3 - C++BugShotGGView Answer on Stackoverflow
Solution 4 - C++mystic_coderView Answer on Stackoverflow