提问人:fredoverflow 提问时间:1/10/2013 最后编辑:Peter Mortensenfredoverflow 更新时间:1/12/2013 访问量:8541
基于范围的“for”循环是否弃用了许多简单的算法?
Does the range-based 'for' loop deprecate many simple algorithms?
问:
算法解决方案:
std::generate(numbers.begin(), numbers.end(), rand);
基于量程的for环路解决方案:
for (int& x : numbers) x = rand();
为什么我要在 C++11 中使用更冗长的基于范围的 for 循环?std::generate
答:
无论 for 循环是否基于范围,它都没有区别,它只是简化了括号内的代码。算法更清晰,因为它们显示了意图。
在我看来,手动循环虽然可能会减少冗长,但缺乏可读性:
for (int& x : numbers) x = rand();
我不会使用这个循环来初始化1 由数字定义的范围,因为当我查看它时,在我看来它正在迭代一个数字范围,但实际上它没有(本质上),即它不是从范围读取,而是写入范围。
当您使用 时,意图会更加清晰。std::generate
1. 在此上下文中初始化意味着为容器的元素提供有意义的值。
评论
std::generate
&
std::generate
第一个版本
std::generate(numbers.begin(), numbers.end(), rand);
告诉我们您要生成一系列值。
在第二个版本中,读者将不得不自己弄清楚。
节省打字通常是次优的,因为它最常在阅读时间上丢失。大多数代码的读取量比键入的要多得多。
评论
numbers
boost::range::generate(numbers, rand);
std::generate(number.begin(), numbers.begin()+3, rand)
number
std::generate()
std::generate(slice(number.begin(), 3), rand)
std::generate(number[0:3], rand)
number
std::generate()
在我看来,有效的 STL 第 43 项:“更喜欢算法调用而不是手写循环”仍然是一个很好的建议。
我通常编写包装函数来摆脱 / 地狱。如果这样做,您的示例将如下所示:begin()
end()
my_util::generate(numbers, rand);
我相信它在传达意图和可读性方面都击败了基于范围的 for 循环。
话虽如此,我必须承认,在 C++98 中,一些 STL 算法调用会产生无法说出的代码,遵循“首选算法调用而不是手写循环”似乎不是一个好主意。幸运的是,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) ) );
带 lambda
auto i=find_if( v.begin(), v.end(), [=](int i) { return i > x && i < y; } );
评论
std::find_if
我的答案是也许和否定。如果我们谈论的是C++11,那么也许(更像是没有)。例如,即使与 lambda 一起使用也很烦人:std::for_each
std::for_each(c.begin(), c.end(), [&](ExactTypeOfContainedValue& x)
{
// do stuff with x
});
但是使用基于范围的 for 要好得多:
for (auto& x : c)
{
// do stuff with x
}
另一方面,如果我们谈论的是C++1y,那么我认为不,算法不会被基于for的范围所淘汰。在C++标准委员会中,有一个研究小组正在研究一项向C++添加范围的提案,并且正在研究多态lambda。范围将消除使用迭代器对的需要,而多态 lambda 将允许您不指定 lambda 的确切参数类型。这意味着可以这样使用(不要把这当成一个铁的事实,这就是今天的梦想的样子):std::for_each
std::for_each(c.range(), [](x)
{
// do stuff with x
});
评论
[]
for_each
for_each
for_each
for_each
parallel_for_each
std::algorithm
就我个人而言,我对以下内容的初步阅读:
std::generate(numbers.begin(), numbers.end(), rand);
是“我们正在分配给一个范围内的所有内容。范围是 。分配的值是随机的”。numbers
我的初步阅读:
for (int& x : numbers) x = rand();
是“我们正在对某个范围内的所有事情做一些事情。范围是 。我们所做的是分配一个随机值。numbers
这些非常相似,但并不完全相同。我可能想要挑起第一次阅读的一个合理原因是,因为我认为关于此代码的最重要的事实是它分配给范围。所以有你的“我为什么要......”。我使用是因为在C++中意味着“范围分配”。顺便说一句,两者之间的区别在于您从中分配的内容。generate
std::generate
std::copy
不过,也有一些混杂因素。与基于迭代器的算法相比,基于范围的 for 循环有一种本质上更直接的方式来表示范围是 。这就是为什么人们在基于范围的算法库上工作的原因:看起来比版本更好。numbers
boost::range::generate(numbers, rand);
std::generate
与此相反,在基于范围的 for 循环中是一个皱纹。如果范围的值类型不是 ,那么我们在这里做了一些令人讨厌的微妙的事情,这取决于它是否可转换为 ,而代码仅取决于可分配给元素的回报。即使值类型是 ,我仍然可能会停下来思考它是否是。因此,它推迟了对类型的思考,直到我看到分配了什么 - 我说“引用范围元素,无论它可能具有什么类型”。回到 C++03 年,算法(因为它们是函数模板)是隐藏确切类型的方法,现在它们是一种方式。int&
int
int&
generate
rand
int
auto
auto &x
我认为一直以来的情况是,最简单的算法与等效循环相比只有边际优势。基于范围的 for 循环改进了循环(主要是通过删除大部分样板,尽管它们还有更多)。因此,利润率越来越小,也许在某些特定情况下您会改变主意。但那里仍然存在风格差异。
评论
operator int&()
int&
SomeClass&
explicit
operator int&()
operator int const &() const
operator int() const
operator=(int)
有些事情你不能(简单地)用基于范围的循环来做,而将迭代器作为输入的算法可以做到。
例如:std::generate
用一个分布中的变量填充容器(排除,是 上的有效迭代器),其余容器填充另一个分布中的变量。limit
limit
numbers
std::generate(numbers.begin(), limit, rand1);
std::generate(limit, numbers.end(), rand2);
基于迭代器的算法使您可以更好地控制您正在操作的范围。
评论
对于 的特殊情况,我同意之前关于可读性/意图问题的回答。std::generate 对我来说似乎是一个更清晰的版本。但我承认,这在某种程度上是一个品味问题。std::generate
也就是说,我还有另一个理由不抛弃 std::algorithm - 有一些算法专门用于某些数据类型。
最简单的例子是 。通用版本在提供的范围内作为 for 循环实现,在实例化模板时将使用此版本。但并非总是如此。例如,如果你给它提供一个范围,它通常会在引擎盖下调用,从而产生更快更好的代码。std::fill
std::vector<int>
memset
所以我试图在这里打一张效率牌。
您的手写循环可能与 std::algorithm 版本一样快,但几乎不可能更快。不仅如此,std::algorithm 可能专门用于特定的容器和类型,并且是在干净的 STL 接口下完成的。
应该注意的一件事是,算法表达的是做了什么,而不是如何做。
基于范围的循环包括完成工作的方式:从第一个元素开始,应用并转到下一个元素,直到最后。即使是一个简单的算法也可以以不同的方式做事(至少对特定容器有一些重载,甚至不考虑可怕的向量),至少它的完成方式不是编写器的业务。
对我来说,这就是很大的区别,尽可能多地封装,并且尽可能地使用算法来证明句子的合理性。
基于范围的 for 循环就是这样。当然,直到标准改变。
算法是一个函数。一个对其参数提出一些要求的函数。这些要求在标准中表述,例如,允许利用所有可用执行线程的实现,并自动加快速度。
下一个:为什么这会调用默认构造函数?
评论
begin()
end()
range
for(auto& x : range(first, last))
)boost::generate(numbers, rand); // ♪