基于范围的“for”循环是否弃用了许多简单的algorithm?

algorithm解答:

std::generate(numbers.begin(), numbers.end(), rand); 

基于范围的循环解决scheme:

 for (int& x : numbers) x = rand(); 

为什么我要在C ++ 11中使用更详细的std::generate基于范围的for循环?

第一个版本

 std::generate(numbers.begin(), numbers.end(), rand); 

告诉我们你想要生成一系列值。

在第二个版本中,读者将不得不自己弄清楚。

节省打字通常是不理想的,因为在阅读时间里经常丢失。 大部分代码的读取量比input的要多得多。

for循环是否基于范围根本没有什么区别,只是简化了括号内的代码。 algorithm更清晰,因为它们显示了意图

就我个人而言,我的初步读物是:

 std::generate(numbers.begin(), numbers.end(), rand); 

是“我们正在分配范围内的所有东西,范围是numbers ,赋值是随机的”。

我的初读:

 for (int& x : numbers) x = rand(); 

是“我们正在做一些事情,范围是numbers我们做的是分配一个随机值。

这些很相似,但不完全相同。 我想引起一读的一个可能的原因是因为我认为关于这个代码的最重要的事实是它被分配到范围。 所以有你的“我为什么要……”。 我使用generate因为在C ++ std::generate意味着“范围分配”。 顺便说一句std::copy ,两者之间的区别是你正在分配。

虽然有混杂的因素。 基于范围的循环有一个固有的更直接的方式来表示范围是numbers ,比基于迭代器的algorithm。 这就是为什么人们使用基于范围的algorithm库: boost::range::generate(numbers, rand); 看起来比std::generate版本更好。

与此int&int&你的基于范围的for循环是一个皱纹。 如果范围的值types不是int ,那么我们在这里做一些恼人的细微操作,这取决于它可以转换为int& ,而generate代码只依赖于rand可以分配给元素的返回值。 即使值types是int ,我仍然可能会停下来思考是否是。 因此, auto ,它推迟考虑的types,直到我看到什么得到分配 – 与auto &x我说“引用范围元素,无论types可能有”。 回到C ++ 03,algorithm(因为它们是函数模板)是隐藏确切types的方法,现在它们是一种方法。

我认为一直以来,最简单的algorithm在等效循环上只有边际效益。 基于范围的for循环可以改善循环(主要是通过去除大部分样板,尽pipe这比他们多一些)。 所以边际收益越来越紧张,或许你在某些特定情况下改变了主意。 但是这里还是有风格差异的。

在我看来,Effective STL Item 43:“首选algorithm调用手写循环”。 仍然是一个很好的build议。

我通常编写包装函数来摆脱begin() / end()地狱。 如果你这样做,你的例子将如下所示:

 my_util::generate(numbers, rand); 

我相信它在循环意图和可读性方面都超越了基于循环的范围。


话虽如此,我必须承认,在C ++ 98中,一些STLalgorithm调用产生了不可改变的代码,并且遵循“首选algorithm调用手写循环”似乎不是一个好主意。 幸运的是,lambda已经改变了这一点。

考虑以下来自Herb Sutter的例子:Lambdas,Lambdas无处不在 。

任务:findv > x< y第一个元素。

没有lambdaexpression式:

 auto i = find_if( v.begin(), v.end(), bind( logical_and<bool>(), bind(greater<int>(), _1, x), bind(less<int>(), _1, y) ) ); 

随着拉姆达

 auto i=find_if( v.begin(), v.end(), [=](int i) { return i > x && i < y; } ); 

看来,手动循环虽然可能减less冗长,但缺乏准确性:

 for (int& x : numbers) x = rand(); 

我不会用这个循环来初始化由数字定义的范围,因为当我看着它时,在我看来,它正在遍历一个数字范围,但实际上它并不(实质上),而不是从范围内读取 ,正在写入范围。

当你使用std::generate时候,意图更加清晰。

1.在这种情况下初始化意味着给容器的元素赋予有意义的价值。

有一些事情你不能做(简单地)与基于范围的循环,algorithm,把迭代器作为input可以。 例如与std::generate

填充容器的limit (排除, limit是一个有效的numbers迭代器)与variables从一个分配,其余与variables从另一个分配。

 std::generate(numbers.begin(), limit, rand1); std::generate(limit, numbers.end(), rand2); 

基于迭代器的algorithm可以更好地控制您正在操作的范围。

对于std::generate的特殊情况,我同意以前对可读性/意图问题的回答。 std :: generate似乎是一个更清晰的版本给我。 但我承认,这是一个有趣的方式。

也就是说,我还有另外一个理由不要扔掉std ::algorithm – 有一些algorithm是专门用于某些数据types的。

最简单的例子是std::fill 。 通用版本在所提供的范围内作为for-loop实现,在实例化模板时将使用此版本。 但不总是。 例如,如果你提供了一个std::vector<int>范围,通常它会调用memset ,产生更快更好的代码。

所以我想在这里玩效率卡。

你的手写循环可能跟std :: algorithm版本一样快,但是它几乎不会更快。 除此之外,std :: algorithm可能专门用于特定的容器和types,并且在干净的STL接口下完成。

我的答案可能会,也可能不会。 如果我们谈论C ++ 11,那么也许(更像是不)。 例如std::for_each真的很烦人,即使使用lambdas也是如此:

 std::for_each(c.begin(), c.end(), [&](ExactTypeOfContainedValue& x) { // do stuff with x }); 

但使用基于范围的是好得多:

 for (auto& x : c) { // do stuff with x } 

另一方面,如果我们谈论的是C ++ 1y,那么我会争辩说不,algorithm不会被基于范围的过时。 在C ++标准委员会中,有一个研究小组正在研究一个为C ++增加范围的提议,同时也正在研究多态lambdaexpression式。 范围将删除需要使用一对迭代器和多形lambda会让你不指定确切的参数types的lambda。 这意味着std::for_each可以像这样使用(不要把这看作是一个很难的事实,而只是今天的梦境):

 std::for_each(c.range(), [](x) { // do stuff with x }); 

有一点需要注意的是algorithmexpression的是什么,而不是如何。

基于范围的循环包括事情的完成方式:从第一个开始,应用下一个元素直到结束。 即使是一个简单的algorithm也可以做不同的事情(至less某些特定容器的重载,甚至不考虑可怕的vector),至less它所做的事情不是作家业务。

对我来说,这是很大的差别,尽可能多地封装,并尽可能地使用algorithm来判断句子。

基于范围的for-loop就是这样。 直到标准改变。

algorithm是一个函数。 对其参数提出一些要求的function。 这些要求在一个标准中被表述为允许例如利用所有可用执行线程的实现,并且将自动加速你。