据我所知,迭代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 ++的新手。)
答案 0 :(得分:5)
我绝对是STL的ga-ga。但我记不起实际上曾经使用for_each
。
成语是
for ( container::iterator it = coll.begin(); it != coll.end(); ++ it )
C ++ 11引入了糖以将其减少为
for ( auto elem : coll )
这类似于您的便利功能,但使用免费(非成员)std::begin
和std::end
函数,这些函数允许与非标准容器的对象兼容。
另外,查一下(我还没有使用它,因为它还没有在GCC中),看起来它限制程序员访问范围的元素,而不是迭代器。
至于使用容器来指代其整个范围,最好保持允许子范围的灵活性。另一种解决方案是为迭代器的对引入一个习惯用法{ begin, end }
。有一些争论,我期望C ++ 11包含一个这样的功能
begin( make_pair( begin_, end_ ) ) // == begin_,
end( make_pair( begin_, end_ ) ) // == end_,
for ( auto elem : make_pair( begin_, end_ ) ) // iterates over [ begin_, end )
但在阅读标准时,pair
似乎缺乏此功能。
但是,您可以创建自己的pair
,以获得灵活的基于范围的for
:
template< typename iter >
struct range_type {
iter first, last;
// use friends because Standard specifies these should be found by ADL:
friend iter begin( range_type const &r ) { return r.first; }
friend iter end( range_type const &r ) { return r.last; }
};
template< typename iter >
range_type< iter > range( iter first, iter last )
{ return range_type< iter >{ first, last }; }
// usage:
for ( auto elem : range( begin_, end_ ) ) // iterates over [ begin_, end )
答案 1 :(得分:2)
指定开始和结束迭代器而不仅仅是集合确实很乏味且容易出错(例如,您的示例代码错误地尝试在.begin()
而不是.end()
上调用a
和v
{1}})。这就是Boost Range被发明的原因。有了它,您就可以编写如下代码:
int a[] = { 1,2,3 };
boost::for_each(a, some_function);
它还引入了范围适配器的概念,它可以用算法组成,以增加它们的有用性和通用性。
[推测] STL使用迭代器而不是范围的原因在于它是从构造算法的角度构思的,然后找到这些算法的最低要求并根据这些要求表达它们。算法需要迭代器来完成它们的工作,即使算法使用的自然事物实际上是值的范围。正如在另一个答案中提到的那样,STL处于严重的时间压力之下,因此这些问题从未得到解决。幸运的是,我们现代人有Boost.Range,所以我们可以使用基于范围的算法。
答案 2 :(得分:1)
你的建议没有任何问题,尽管我不得不说Boost.ForEach与现在的C ++世界中的事物接近“惯用语”。
这种方法的好处,在使用中看起来像:
BOOST_FOREACH(value_type i, collection) {
function(i);
}
是您也可以内联您的操作,而不是严格限制显式功能映射。
答案 3 :(得分:1)
原因是STL的设计者来自理论方面。在Comp.Sci中,范围是一个比容器更基本的概念。在实践中,容器更为常见。 STL是在1996 - 1998年的严重时间压力下加入的,并没有积极地进行重构。
您在std::for_each
的第三个参数中看到了类似的问题。理论上,lambda存在。在C ++ 98中,他们没有。因此,您必须在线外定义仿函数,对绑定器和合成器使用详细语法,或使用无状态指针到函数。
就个人而言,我认为所有STL算法都应该采用一个范围(单个对象),而不是一对迭代器。将后者包含在一个范围内是微不足道的。现在你看到ostream_iterators必须定义一个相当任意的结束对象。