为什么总是在 std::for_each 中指定迭代器?
据我所知,迭代 STL 集合的习惯用法看起来像这样:
int a[] = { 1,2,3 };
std::vector<int> v(a, a+3);
std::for_each(a.begin(), a.end(), some_function);
如果我只想在集合的某个范围上工作,或者做一些更有创意的事情,那么指定第一个和最后一个迭代器很有用,但大多数情况下当时,我怀疑我们实际上想与整个系列合作。所以,我想知道为什么人们在这种情况下费心指定迭代器(因为它们总是相同的),并且不只是使用这些方便的函数:(
namespace easy
{
template <class T, class F>
F for_each(T& collection, F function)
{
std::for_each(collection.begin(), collection.end(), function);
return function;
}
}
当然,这可能是这是惯用的做事方式,但我从来没有注意到我是 C++ 新手。)
As far as I can tell, the idiom for iterating over STL collections looks something like this:
int a[] = { 1,2,3 };
std::vector<int> v(a, a+3);
std::for_each(a.begin(), a.end(), some_function);
Specifying the first and last iterators is useful if I want to only work on a certain range of the collection, or do something more creative, but most of the time, I suspect we're actually wanting to work with the whole collection. So, I'm wondering why people bother specifying the iterators in that situation (since they'll always be the same), and don't just use a convenience function along these lines:
namespace easy
{
template <class T, class F>
F for_each(T& collection, F function)
{
std::for_each(collection.begin(), collection.end(), function);
return function;
}
}
(Of course, it's possible that this is the idiomatic way of doing things and I never noticed! I'm new to C++, though.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(4)
指定开始和结束迭代器而不仅仅是集合确实很乏味且容易出错(例如您的示例代码错误地尝试调用 .begin()
和 .end()
在 a
而不是 v
上)。这就是发明 Boost Range 的原因。有了它,您可以编写如下代码:
int a[] = { 1,2,3 };
boost::for_each(a, some_function);
它还引入了范围适配器的概念,可以将其与算法组合以倍增其有用性和通用性。
[推测] STL 使用迭代器而不是范围的原因是,它是从构造算法、然后找到这些算法的最低要求并根据这些要求来表达它们的角度来构思的。算法需要迭代器才能完成它们的工作,尽管算法使用的自然对象实际上是值的范围。正如另一个答案中提到的,STL 面临着严重的时间压力,因此这些问题从未得到解决。幸运的是,我们现代人有 Boost.Range,所以我们可以使用基于范围的算法。
你的建议没有任何问题,但我不得不说 Boost.ForEach 是当今 C++ 世界中最接近“惯用”的东西。
这种方法的好处(在使用中看起来类似于:)
BOOST_FOREACH(value_type i, collection) {
function(i);
}
是您还可以内联操作,而不是严格绑定到显式函数映射。
原因在于STL的设计者来自理论方面。在 Comp.Sci 中,范围是比容器更基本的概念。在实践中,容器更为常见。 STL 是在 1996-1998 年迫于严重的时间压力而添加的,并且没有进行积极的重构。
您会在 std::for_each
的第三个参数中看到类似的问题。理论上,lambda 是存在的。在 C++98 中,他们没有。因此,您必须定义一个不合规矩的函子,使用带有绑定器和合成器的详细语法,或者使用无状态函数指针。
就我个人而言,我认为所有STL算法都应该采用一个范围(单个对象),而不是一对迭代器。将后者包装在一个范围内很简单。现在您看到 ostream_iterators 必须定义一个相当任意的最终对象。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
我绝对对 STL 非常着迷。但我不记得实际上曾经使用过
for_each
。习惯用法是
C++11 引入糖将其减少为 这
与您的便利函数类似,但使用免费(非成员)
std::begin
和std::end 允许与非标准容器的对象兼容的函数。
另外,查了一下(我还没有玩过这个,因为它还没有在 GCC 中),看起来它限制程序员访问范围的元素,而不是迭代器。
至于使用容器来引用其整个范围,最好保持允许子范围的灵活性。另一种解决方案是为一对迭代器引入一个习惯用法,
{ begin, end }
。有一些争论,我预计 C++11 会包含这样的功能,但在阅读标准后,
pair
似乎缺乏此功能。不过,您可以创建自己的此类
pair
,以获得灵活的基于范围的for
:I'm totally ga-ga for STL, absolutely. But I can't recall actually ever using
for_each
.The idiom is
C++11 introduces sugar to reduce this to
This is similar to your convenience function, but uses free (non-member)
std::begin
andstd::end
functions that allow compatibility with objects that are not standard containers.Also, looking it up (I haven't gotten to play with this as it's not in GCC yet), it looks like it restricts the programmer to accessing elements of the range, not iterators into it.
As for using the container to refer to the entirety of its range, it's much preferable to maintain the flexibility to allow subranges. The alternative solution is to introduce an idiom for a pair of iterators,
{ begin, end }
. There was some debate, and I expected C++11 to include a feature such thatbut upon reading the Standard it looks like
pair
lacks this functionality.You can create your own such
pair
, however, to obtain the flexible range-basedfor
: