我应该使用std :: for_each吗?

Ala*_*ing 42 c++ foreach lambda for-loop stl

我总是试图更多地了解我使用的语言(不同的样式,框架,模式等).我注意到我从未使用过,std::for_each所以我想也许我应该开始.在这种情况下的目标是扩展我的思想,而不是在某种程度上改进代码(可读性,表达性,紧凑性等).

因此,考虑到上下文,使用std::for_each简单的任务是一个好主意,比如打印出一个向量:

for_each(v.begin(), v.end(), [](int n) { cout << n << endl; }
Run Code Online (Sandbox Code Playgroud)

([](int n)作为lambda函数).代替:

for(int i=0; i<v.size(); i++) { cout << v[i] << endl; }
Run Code Online (Sandbox Code Playgroud)

我希望这个问题似乎没有意义.我想这几乎问了一个更大的问题......应的中级程序员使用的语言特性,即使他并不真的需要在这个时候只是让他能够理解该功能的时候,实际上可能会受益非浅更好它.虽然可能已经提出了这个更大的问题(例如这里).

Jam*_*lis 46

使用std::for_each而不是旧学校for循环(甚至是新奇的C++ 0x范围for循环)是有利的:您可以查看语句的第一个单词,并确切地知道语句的作用.

当您看到时for_each,您知道lambda中的操作只对该范围中的每个元素执行一次(假设没有抛出异常).在处理每个元素之前不可能在循环之前中断循环,并且不可能跳过元素或多次为一个元素计算循环体.

使用for循环,您必须阅读循环的整个主体以了解它的作用.它可能有continue,breakreturn在其中声明改变控制流程.它可能包含修改迭代器或索引变量的语句.没有检查整个循环就没有办法知道.

Herb Sutter 在最近向Northwest C++ Users Group的演示中讨论了使用算法和lambda表达式的优点.

请注意,std::copy如果您愿意,可以在此处使用此算法:

std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, "\n"));
Run Code Online (Sandbox Code Playgroud)

  • 在`for_each`中相当于`continue`只是`return`. (3认同)
  • 由于提问者有C++ 0x,大概你可以做`std :: ostream_iterator <decltype(*v.begin())>`.不确定我是否喜欢它,可能只是用另一个来代替一个烦恼. (2认同)

Xeo*_*Xeo 24

这取决于.

功能for_each是,您可以将它与任何其迭代器满足输入迭代器概念的容器一起使用,因此它通常可用于任何容器.这样可以提高可维护性,只需更换容器即可,无需更改任何内容.对于size矢量的循环,情况也是如此.您可以在不必更改循环的情况下交换它的唯一其他容器将是另一个随机访问的容器.

现在,如果您自己输入迭代器版本,典型版本如下所示:

// substitute 'container' with a container of your choice
for(std::container<T>::iterator it = c.begin(); it != c.end(); ++it){
  // ....
}
Run Code Online (Sandbox Code Playgroud)

相当冗长,嗯?C++ 0x使用auto关键字减轻了我们的长度:

for(auto it = c.begin(); it != c.end(); ++it){
  // ....
}
Run Code Online (Sandbox Code Playgroud)

已经更好,但仍然不完美.您正在调用end每次迭代,并且可以做得更好:

for(auto it = c.begin(), ite = c.end(); it != ite; ++it){
  // ....
}
Run Code Online (Sandbox Code Playgroud)

现在看起来不错.仍然比同等for_each版本更长:

std::for_each(c.begin(), c.end(), [&](T& item){
  // ...
});
Run Code Online (Sandbox Code Playgroud)

"等效"略微主观,因为Tlambda的参数列表中可能有一些冗长的类型my_type<int>::nested_type.虽然,typedef他/她可以解决这个问题.老实说,我仍然不明白为什么lambda不允许带类型演绎的多态...


现在,要考虑的另一件事是for_each,名称本身已经表达了意图.它表示序列中不会跳过任何元素,这可能与正常的for循环有关.

这让我想到了另一点:因为for_each打算在整个序列上运行并对容器中的每个项目应用一个操作,所以它不是为了处理早期的returns或breaks 而设计的.continue可以使用returnlambda/functor中的语句进行模拟.

因此,请使用for_each真正想要对集合中的每个项目应用操作的位置.

在旁注中,for_each由于基于范围的令人敬畏的for循环(也称为foreach循环),可能只是对C++ 0x"弃用":

for(auto& item : container){
  // ...
}
Run Code Online (Sandbox Code Playgroud)

哪个更短(yay)并允许以下所有三个选项:

  • 提前返回(即使返回值!)
  • 打破循环和
  • 跳过一些元素.


Bil*_*eal 9

我一般会建议使用std::for_each.您的循环示例不适用于非随机访问容器.您可以使用迭代器编写相同的循环,但由于写出std::SomeContainerName<SomeReallyLongUserType>::const_iterator迭代变量的类型,通常会很痛苦.std::for_each将您与此隔离,并end自动将呼叫分摊.

  • 他不能使用`auto`作为`std :: SomeContainerName <SomeReallyLongUserType> :: const_iterator`.(OP已经在使用C++ 0x \). (7认同)

iam*_*ind 8

恕我直言,您应该在测试代码中尝试这些新功能.

生产代码中,您应该尝试您感觉舒适的功能.(即如果你觉得舒服for_each,你可以使用它.)