std :: transform和std :: for_each有什么区别?

ben*_*der 52 c++ stl-algorithm c++11

两者都可用于将函数应用于一系列元素.

在高层次上:

  • std::for_each 忽略函数的返回值,并保证执行的顺序.
  • std::transform 将返回值赋给迭代器,并不保证执行的顺序.

你什么时候喜欢使用那个?有任何微妙的警告吗?

Ili*_*llo 51

std::transform是一样的map.我们的想法是将函数应用于两个迭代器之间的每个元素,并获得由应用此类函数产生的元素组成的不同容器.您可能希望将其用于,例如,将对象的数据成员投影到新容器中.在下文中,std::transform用于转换std::strings容器中的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();});
Run Code Online (Sandbox Code Playgroud)

另一方面,您执行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;
});
Run Code Online (Sandbox Code Playgroud)

实际上,从C++ 11开始,使用基于范围的for循环的terser表示法可以实现相同的目的:

for (std::size_t name_size: name_sizes) {
    std::cout << name_size << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

  • 为了对此进行扩展,在C++ 14中我们可以从基于for循环的范围中删除类型到现在为:for(name_size:name_sizes){...} (4认同)
  • @TrevorHickey我很快又检查了一次,除了原始建议N3853,我找不到任何新闻。你确定吗? (2认同)

Nia*_*all 22

您的高级概述

  • std::for_each 忽略函数的返回值并保证执行顺序.
  • std::transform 将返回值赋给迭代器,并不保证执行的顺序.

几乎涵盖了它.

另一种看待它的方式(优先选择另一种方式);

  • 操作的结果(返回值)是否重要?
  • 每个元素上的操作是否为没有返回值的成员方法?
  • 有两个输入范围吗?

还要记住的另一件事(微妙的警告)是std::transformC++ 11之前和之后操作要求的变化(来自en.cppreference.com);

  • 在C++ 11之前,他们被要求"没有任何副作用",
  • 在C++ 11之后,这变为"必须不使任何迭代器无效,包括结束迭代器,或修改所涉及范围的任何元素"

基本上这些是允许未确定的执行顺序.

我何时使用一个而不是另一个?

如果我想操纵范围中的每个元素,那么我使用for_each.如果我必须从每个元素计算一些东西,那么我会使用transform.当使用for_each和时transform,我通常将它们与lambda配对.

也就是说,for_each自从forC++ 11(for (element : range))中基于范围的循环和lambdas 的出现以来,我发现我对传统的当前用法有所减少.我发现它的语法和实现非常自然(但你的里程数会有所不同),并且更适合某些用例.

  • 这与“for_each”算法相反,它需要按顺序将函数应用于每个元素。“转换”操作要求不产生任何副作用,这意味着执行顺序无法观察到,因此无法保证。总而言之,我看过的实现都按照预期的顺序(从头到尾)应用它。 (2认同)

Bug*_*tGG 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;
}
Run Code Online (Sandbox Code Playgroud)

将打印:

1, 2, 3, 4, 
-1, -2, -3, -4, 
Run Code Online (Sandbox Code Playgroud)

  • @eigenfield 方面的诡计如何。这就是它的定义方式以及它的使用方式。找到解决方法并不能改变这一点。 (3认同)
  • 您对“可修改性”方面的强调是骗人的。因为您承认可以使用“for_each”来修改某些内容,尽管这样做并不惯用。综上所述,“for_each”和“transform”确实可以根据自己的说法进行“修改”。因此,“可修改性”并不是区分它们的决定性特征。我宁愿强调“顺序”,而不是强调“可修改性”,因为这是基于规范的绝对差异化因素。 (2认同)