Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 333
bendervader Avatar asked Jun 26 '15 04:06

bendervader


People also ask

What is std:: transform?

std::transformApplies an operation sequentially to the elements of one (1) or two (2) ranges and stores the result in the range that begins at result . (1) unary operation. Applies op to each of the elements in the range [first1,last1) and stores the value returned by each operation in the range that begins at result ...

Is std :: transform faster?

I timed the difference between both implementations using google benchmark and came to the conclusion that the loop is about 5 times faster than using std::transform.


2 Answers

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; } 
like image 98
Ilio Catallo Avatar answered Oct 13 '22 08:10

Ilio Catallo


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.

like image 39
Niall Avatar answered Oct 13 '22 06:10

Niall