fre*_*low 81 c++ algorithm foreach stl c++11
算法解决方案
std::generate(numbers.begin(), numbers.end(), rand);
Run Code Online (Sandbox Code Playgroud)
基于范围的for-loop解决方案:
for (int& x : numbers) x = rand();
Run Code Online (Sandbox Code Playgroud)
为什么我要std::generate在C++ 11中使用更冗长的基于范围的for循环?
Bo *_*son 78
第一个版本
std::generate(numbers.begin(), numbers.end(), rand);
Run Code Online (Sandbox Code Playgroud)
告诉我们您要生成一系列值.
在第二个版本中,读者必须自己解决这个问题.
节省打字通常不是最理想的,因为它通常在阅读时间中丢失.大多数代码的读取次数比输入的多得多.
Ste*_*sop 30
就个人而言,我的初步读物是:
std::generate(numbers.begin(), numbers.end(), rand);
Run Code Online (Sandbox Code Playgroud)
是"我们正在分配范围内的所有东西.范围是numbers.分配的值是随机的".
我的初步读物:
for (int& x : numbers) x = rand();
Run Code Online (Sandbox Code Playgroud)
是"我们正在对范围内的所有事情做点什么.范围是numbers.我们做的是分配一个随机值."
这些非常类似,但不完全相同.我可能想要引起一读的一个合理的原因是因为我认为关于这个代码的最重要的事实是它分配给范围.所以,你的"我为什么要...".我用,generate因为在C++中std::generate意味着"范围分配".顺便说一句std::copy,两者之间的差异就是你所分配的.
但是有一些令人困惑的因素.与基于numbers迭代器的算法相比,基于范围的for循环具有表达范围的固有更直接的方式.这就是为什么人们使用基于范围的算法库:boost::range::generate(numbers, rand);看起来比std::generate版本更好.
与此相反,int&在你的基于范围的for循环中是一种皱纹.如果范围的值类型不是int,那么我们在这里做一些非常微妙的事情取决于它可以转换为int&,而generate代码只取决于rand可分配给元素的返回.即使价值类型是int,我仍然可能会停下来考虑它是否是.因此auto,在我看到什么被分配之前,它推迟了对类型的思考 - auto &x我说"引用范围元素,无论可能具有什么类型".早在C++ 03,算法(因为它们是函数模板)都将隐藏确切类型的方式,现在他们一个方式.
我认为最简单的算法一直都是等效循环的边际优势.基于范围的for循环改善了循环(主要是通过删除大部分样板,尽管它们比它更多).所以利润率越来越紧,也许你会在某些特定情况下改变主意.但那里还有一个风格差异.
Ali*_*Ali 23
在我看来,有效STL第43项:"首选算法调用手写循环".仍然是一个很好的建议.
我通常写包装函数来摆脱begin()/ end()地狱.如果你这样做,你的例子将如下所示:
my_util::generate(numbers, rand);
Run Code Online (Sandbox Code Playgroud)
我相信它在传达意图和可读性方面优于循环范围.
话虽如此,我必须承认,在C++ 98中,一些STL算法调用产生了无法解释的代码,并且遵循"Prefer算法调用手写循环"似乎不是一个好主意.幸运的是,lambdas改变了这一点.
请考虑Herb Sutter的以下示例:Lambdas,Lambdas Everywhere.
任务:找到v中的第一个元素是> x和< y.
没有lambdas:
auto i = find_if( v.begin(), v.end(),
bind( logical_and<bool>(),
bind(greater<int>(), _1, x),
bind(less<int>(), _1, y) ) );
Run Code Online (Sandbox Code Playgroud)
随着lambda
auto i=find_if( v.begin(), v.end(), [=](int i) { return i > x && i < y; } );
Run Code Online (Sandbox Code Playgroud)
Naw*_*waz 22
在我看来,手动循环虽然可能会减少冗长,但缺乏准确性:
for (int& x : numbers) x = rand();
Run Code Online (Sandbox Code Playgroud)
我不会使用这个循环来初始化1由数字定义的范围,因为当我看到它时,在我看来它是在一系列数字上迭代,但实际上它不是(本质上),即代替从该范围读取,它写入范围.
使用时意图更加清晰std::generate.
1. 在此上下文中初始化意味着为容器的元素赋予有意义的价值.
有些事情你不能(简单地)使用基于范围的循环,这些算法将迭代器作为输入.例如std::generate:
使用来自一个分布的变量填充容器limit(排除,limit是一个有效的迭代器numbers),其余来自另一个分布的变量.
std::generate(numbers.begin(), limit, rand1);
std::generate(limit, numbers.end(), rand2);
Run Code Online (Sandbox Code Playgroud)
基于迭代器的算法可以更好地控制您操作的范围.
对于特定情况std::generate,我同意先前关于可读性/意图问题的答案.std :: generate对我来说似乎是一个更清晰的版本.但我承认这在某种程度上是一种品味问题.
也就是说,我有另一个理由不抛弃std :: algorithm - 某些算法专门用于某些数据类型.
最简单的例子是std::fill.通用版本在提供的范围内实现为for循环,并且在实例化模板时将使用此版本.但不总是.例如,如果你将它提供一个范围std::vector<int>- 通常它实际上会memset在引擎盖下调用,产生更快更好的代码.
所以我在这里尝试效率卡.
您的手写循环可能与std :: algorithm版本一样快,但它几乎不会更快.除此之外,std :: algorithm可能专门用于特定的容器和类型,它是在干净的STL接口下完成的.