两者都可用于将函数应用于一系列元素。
高层次:
std::for_each
忽略函数的返回值,并且
保证执行的顺序。std::transform
将返回值赋给迭代器,并且确实如此
不保证执行的顺序。您何时喜欢使用其中一种?有什么微妙的警告吗?
答案 0 :(得分:45)
std::transform
与map
相同。我们的想法是将函数应用于两个迭代器之间的每个元素,并获得由应用此类函数产生的元素组成的不同容器。您可能希望将其用于例如将对象的数据成员投影到新容器中。在下文中,std::transform
用于在std::string
s的容器中转换std::size_t
s的容器。
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
循环的terser符号可以实现相同的目的:
for (std::size_t name_size: name_sizes) {
std::cout << name_size << std::endl;
}
答案 1 :(得分:18)
您的高级概述
std::for_each
忽略函数的返回值并保证执行顺序。std::transform
将返回值赋给迭代器,但不保证执行顺序。
几乎涵盖了它。
另一种看待它的方法(优先选择其他方式);
要记住的另一件事(微妙的警告)是C ++ 11之前和之后std::transform
操作要求的变化(来自en.cppreference.com) );
基本上这些是允许执行的未定顺序。
我何时使用其中一个?
如果我想操作范围中的每个元素,那么我使用for_each
。如果我必须从每个元素计算一些东西,那么我会使用transform
。 使用for_each
和transform
时,我通常会将它们与lambda配对。
也就是说,自从C ++ 11(for_each
)中基于范围的for
循环和lambdas的出现以来,我发现我对传统for (element : range)
的当前用法有所减少。我发现它的语法和实现非常自然(但你的里程数会有所不同),并且更适合某些用例。
答案 2 :(得分:13)
虽然这个问题已得到解答,但我相信这个例子可以进一步澄清其中的差异。
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,
答案 3 :(得分:1)
使用std :: tranform的真实示例是当你想将字符串转换为大写时,你可以编写如下代码:
std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
如果你试图用std :: for_each来实现同样的目的,那就是:
std::for_each(s.begin(), s.end(), ::toupper);
它不会将其转换为大写字符串