基于范围的'for'循环是否弃用了许多简单的算法?

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)

告诉我们您要生成一系列值.

在第二个版本中,读者必须自己解决这个问题.

节省打字通常不是最理想的,因为它通常在阅读时间中丢失.大多数代码的读取次数比输入的多得多.

  • "*节省打字通常是次优的*"胡说八道; 这都是关于你正在使用的库.std :: generate很长,因为你必须无缘无故地指定两次`numbers`.因此:`boost :: range :: generate(numbers,rand);`.没有理由你不能在一个精心构建的库中同时拥有更短和更易读的代码. (25认同)
  • 节省打字?哦,我明白了.为什么我们为什么在"编译时健全性检查"和"在键盘上敲击键"中使用相同的术语?:) (13认同)
  • 这一切都在读者的眼中.对于循环版本,大多数编程背景都是可以理解的:将rand值放入集合的每个元素.Std :: generate需要知道最近的C++,或者猜测生成实际意味着"修改项目",而不是"返回生成的值". (9认同)
  • @MarsonMao:如果你只有一个双参数`std :: generate()`,你可以改为`std :: generate(slice(number.begin(),3),rand)`或更好的假设范围切片语法,如`std :: generate(number [0:3],rand)`,它删除了`number`的重复,同时仍允许灵活地指定部分范围.从三个参数`std :: generate()`开始反向更加乏味. (7认同)
  • 如果你只想修改容器的一部分那么你可以`std :: generate(number.begin(),numbers.begin()+ 3,rand)`,不是吗?所以我猜两次指定`number`可能有用. (2认同)

Dav*_*eas 42

for循环是否基于范围并没有产生任何影响,它只是简化了括号内的代码.算法更清晰,因为它们表明了意图.


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)

  • 与问题有点正交。只有第一句话解决了这个问题。 (2认同)

Naw*_*waz 22

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

for (int& x : numbers) x = rand();
Run Code Online (Sandbox Code Playgroud)

我不会使用这个循环来初始化1数字定义的范围,因为当我看到它时,在我看来它是一系列数字上迭代,但实际上它不是(本质上),即代替从该范围读取,它写入范围.

使用时意图更加清晰std::generate.

1. 在此上下文中初始化意味着为容器的元素赋予有意义的价值.

  • 这不仅仅是因为你不习惯基于范围的for循环吗?我觉得这个陈述对范围内的每个元素都有所分配.很明显,如果您熟悉`std :: generate`,那么生成也是一样的,可以假定它是C++程序员(如果他们不熟悉,他们会查找,同样的结果). (5认同)
  • @SteveJessop:这个答案与其他两个没有什么不同.它需要读者更多的努力,并且更容易出错(如果忘记单个`&`字符会怎样?)算法的优点是它们显示意图,而使用循环则必须推断它.如果循环的实现中存在错误,则不清楚它是否是错误或是故意的. (4认同)

Kha*_*aur 9

有些事情你不能(简单地)使用基于范围的循环,这些算法将迭代器作为输入.例如std::generate:

使用来自一个分布的变量填充容器limit(排除,limit是一个有效的迭代器numbers),其余来自另一个分布的变量.

std::generate(numbers.begin(), limit, rand1);
std::generate(limit, numbers.end(), rand2);
Run Code Online (Sandbox Code Playgroud)

基于迭代器的算法可以更好地控制您操作的范围.

  • 虽然可读性原因是偏好算法的**巨大**,但这是唯一一个显示_range-based for-loop_只是_algorithms_的子集的答案,因此不能_deprecate_任何东西...... (8认同)

Ser*_*sov 6

对于特定情况std::generate,我同意先前关于可读性/意图问题的答案.std :: generate对我来说似乎是一个更清晰的版本.但我承认这在某种程度上是一种品味问题.

也就是说,我有另一个理由不抛弃std :: algorithm - 某些算法专门用于某些数据类型.

最简单的例子是std::fill.通用版本在提供的范围内实现为for循环,并且在实例化模板时将使用此版本.但不总是.例如,如果你将它提供一个范围std::vector<int>- 通常它实际上会memset在引擎盖下调用,产生更快更好的代码.

所以我在这里尝试效率卡.

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