C++ std::transform 和 std::for_each 有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31064749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the difference between std::transform and std::for_each?
提问by bendervader
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.
std::for_each
忽略函数的返回值,并保证执行顺序。std::transform
将返回值赋给迭代器,不保证执行顺序。
When do you prefer using the one versus the other? Are there any subtle caveats?
你什么时候更喜欢使用一种而不是另一种?有什么微妙的警告吗?
采纳答案by Ilio Catallo
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::string
s in a container of std::size_t
s.
std::transform
与 相同map
。这个想法是对两个迭代器之间的每个元素应用一个函数,并获得由应用该函数产生的元素组成的不同容器。您可能希望将其用于,例如,将对象的数据成员投影到新容器中。下面,std::transform
用于将std::string
s 的容器转换为s 的容器std::size_t
。
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.
另一方面,您执行std::for_each
唯一的副作用。换句话说,std::for_each
非常类似于一个普通的基于范围的for
循环。
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:
事实上,从 C++11 开始,同样可以通过使用基于范围的for
循环的更简洁的表示法来实现:
for (std::size_t name_size: name_sizes) {
std::cout << name_size << std::endl;
}
回答by Niall
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.
std::for_each
忽略函数的返回值并保证执行顺序。std::transform
将返回值赋给迭代器,不保证执行顺序。
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);
要记住的另一件事(微妙的警告)是std::transform
C++11 之前和之后的操作要求的变化(来自 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"
- 在 C++11 之前,它们被要求“没有任何副作用”,
- 在 C++11 之后,这更改为“不得使任何迭代器无效,包括结束迭代器,或修改所涉及范围的任何元素”
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.
如果我想操作一个范围内的每个元素,那么我使用for_each
. 如果我必须从每个元素计算一些东西,那么我会使用transform
. 使用for_each
and 时transform
,我通常将它们与 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.
也就是说,我发现for_each
自从for
C++11 ( for (element : range)
) 中基于范围的循环和 lambda出现以来,我目前对传统的使用有所减少。我发现它的语法和实现非常自然(但您在这里的里程会有所不同)并且更适合某些用例。
回答by BugShotGG
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.
for_each
属于非修改 STL 操作,意味着这些操作不会改变集合的元素或集合本身。因此,for_each 返回的值总是被忽略并且不会分配给集合元素。尽管如此,仍然可以修改集合的元素,例如当使用引用将元素传递给 f 函数时。应该避免这种行为,因为它不符合 STL 原则。
In contrast, transform
function belongs to modifying STL operationsand applies given predicates (unary_op or binary_op) to elements of the collection or collections and store results in another collection.
相比之下,transform
函数属于修改 STL 操作,并将给定的谓词(unary_op 或 binary_op)应用于一个或多个集合的元素,并将结果存储在另一个集合中。
#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,
回答by mystic_coder
Real example of using std::tranform is when you want to convert a string to uppercase, you can write code like this :
使用 std::tranform 的真实示例是,当您想将字符串转换为大写时,您可以编写如下代码:
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 实现相同的目标,例如:
std::for_each(s.begin(), s.end(), ::toupper);
It wont convert it into uppercase string
它不会将其转换为大写字符串