std::transform 和 std::for_each 有什么区别?
What is the difference between std::transform and std::for_each?
两者都可用于将函数应用于一系列元素。
高层次:
std::for_each
忽略函数的 return 值,并且
保证执行顺序。
std::transform
将 return 值分配给迭代器,并执行
不保证执行顺序。
你什么时候更喜欢使用一个而不是另一个?有什么微妙的注意事项吗?
使用std::tranform的真实例子是当你想将字符串转换为大写时,你可以这样写代码:
std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
如果你想用 std::for_each 达到同样的效果,比如:
std::for_each(s.begin(), s.end(), ::toupper);
它不会将其转换为大写字符串
std::transform
等同于 map
。这个想法是对两个迭代器之间的每个元素应用一个函数,并获得一个不同的容器,该容器由应用这种函数产生的元素组成。您可能希望将其用于例如将对象的数据成员投影到新容器中。下面用std::transform
把一个std::string
的容器转换成一个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();});
另一方面,您执行 std::for_each
是为了唯一的副作用。换句话说,std::for_each
非常类似于普通的基于范围的 for
循环。
返回字符串示例:
std::for_each(name_sizes.begin(), name_sizes.end(), [](std::size_t name_size) {
std::cout << name_size << std::endl;
});
确实,从 C++11 开始,使用基于范围的 for
循环的简洁符号可以实现相同的目的:
for (std::size_t name_size: name_sizes) {
std::cout << name_size << std::endl;
}
您的高级概览
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.
几乎涵盖了它。
另一种看待它的方式(比另一个更喜欢一个);
- 操作的结果(return 值)重要吗?
- 对每个元素的操作都是没有return值的成员方法吗?
- 是否有两个输入范围?
还有一件事要记住(微妙的警告)是 C++11 之前和之后 std::transform
的操作要求的变化(来自en.cppreference.com);
- 在 C++11 之前,它们需要 "not have any side effects"、
- 在 C++11 之后,这改为 "must not invalidate any iterators, including the end iterators, or modify any elements of the ranges involved"
基本上这些都是为了允许未确定的执行顺序。
我什么时候用一个代替另一个?
如果我想操作一个范围内的每个元素,那么我使用for_each
。如果我必须从每个元素计算一些东西,那么我会使用 transform
。 当使用 for_each
和 transform
时,我通常将它们与 lambda 配对。
也就是说,我发现自从 C++11 中基于范围的 for
循环和 lambda 出现以来,我目前对传统 for_each
的使用有所减少(for (element : range)
).我发现它的语法和实现非常自然(但你在这里的里程会有所不同)并且更直观地适合某些用例。
虽然问题已经得到解答,但我相信这个例子会进一步阐明差异。
for_each
属于非修改STL操作,意味着这些操作不会改变集合的元素或集合本身。因此,for_each 返回的 值始终被忽略,不会分配给集合元素 。
尽管如此,仍然可以修改集合的元素,例如,当使用引用将元素传递给 f 函数时。应避免这种行为,因为它不符合 STL 原则。
相比之下,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;
}
这将打印:
1, 2, 3, 4,
-1, -2, -3, -4,
两者都可用于将函数应用于一系列元素。
高层次:
std::for_each
忽略函数的 return 值,并且 保证执行顺序。std::transform
将 return 值分配给迭代器,并执行 不保证执行顺序。
你什么时候更喜欢使用一个而不是另一个?有什么微妙的注意事项吗?
使用std::tranform的真实例子是当你想将字符串转换为大写时,你可以这样写代码:
std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
如果你想用 std::for_each 达到同样的效果,比如:
std::for_each(s.begin(), s.end(), ::toupper);
它不会将其转换为大写字符串
std::transform
等同于 map
。这个想法是对两个迭代器之间的每个元素应用一个函数,并获得一个不同的容器,该容器由应用这种函数产生的元素组成。您可能希望将其用于例如将对象的数据成员投影到新容器中。下面用std::transform
把一个std::string
的容器转换成一个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();});
另一方面,您执行 std::for_each
是为了唯一的副作用。换句话说,std::for_each
非常类似于普通的基于范围的 for
循环。
返回字符串示例:
std::for_each(name_sizes.begin(), name_sizes.end(), [](std::size_t name_size) {
std::cout << name_size << std::endl;
});
确实,从 C++11 开始,使用基于范围的 for
循环的简洁符号可以实现相同的目的:
for (std::size_t name_size: name_sizes) {
std::cout << name_size << std::endl;
}
您的高级概览
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.
几乎涵盖了它。
另一种看待它的方式(比另一个更喜欢一个);
- 操作的结果(return 值)重要吗?
- 对每个元素的操作都是没有return值的成员方法吗?
- 是否有两个输入范围?
还有一件事要记住(微妙的警告)是 C++11 之前和之后 std::transform
的操作要求的变化(来自en.cppreference.com);
- 在 C++11 之前,它们需要 "not have any side effects"、
- 在 C++11 之后,这改为 "must not invalidate any iterators, including the end iterators, or modify any elements of the ranges involved"
基本上这些都是为了允许未确定的执行顺序。
我什么时候用一个代替另一个?
如果我想操作一个范围内的每个元素,那么我使用for_each
。如果我必须从每个元素计算一些东西,那么我会使用 transform
。 当使用 for_each
和 transform
时,我通常将它们与 lambda 配对。
也就是说,我发现自从 C++11 中基于范围的 for
循环和 lambda 出现以来,我目前对传统 for_each
的使用有所减少(for (element : range)
).我发现它的语法和实现非常自然(但你在这里的里程会有所不同)并且更直观地适合某些用例。
虽然问题已经得到解答,但我相信这个例子会进一步阐明差异。
for_each
属于非修改STL操作,意味着这些操作不会改变集合的元素或集合本身。因此,for_each 返回的 值始终被忽略,不会分配给集合元素 。
尽管如此,仍然可以修改集合的元素,例如,当使用引用将元素传递给 f 函数时。应避免这种行为,因为它不符合 STL 原则。
相比之下,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;
}
这将打印:
1, 2, 3, 4,
-1, -2, -3, -4,