为什么总是在std::for_each中指定迭代器?
Why always specify iterators in 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++新手。)
我非常喜欢STL。但是我不记得实际上曾经使用过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 )
指定开始和结束迭代器,而不是仅仅指定集合,确实很乏味,而且容易出错(例如,您的示例代码错误地试图在a
而不是v
上调用.begin()
和.end()
)。这就是Boost Range被发明的原因。有了它,您可以编写如下代码:
int a[] = { 1,2,3 };
boost::for_each(a, some_function);
还介绍了范围适配器的概念,它可以与算法组成,以增加其有用性和通用性。
[推测]STL使用迭代器而不是范围的原因是,它是从构造算法的角度出发的,然后找到这些算法的最小需求,并用这些需求来表达它们。算法需要迭代器来完成它们的工作,即使算法使用的自然对象实际上是值的范围。正如在另一个答案中提到的,STL面临着严重的时间压力,因此这些问题从未得到解决。幸运的是,我们现代人有Boost。范围,所以我们可以使用基于范围的算法
你的建议没有错,尽管我不得不说Boost。在当今的c++世界中,ForEach几乎是最接近"惯用"的了。
这种方法的好处,它在使用中看起来像:
BOOST_FOREACH(value_type i, collection) {
function(i);
}
是你也可以内联你的操作,而不是严格地绑定到一个显式的函数映射。
原因是STL的设计者来自理论方面。在Comp.Sci中,范围是一个比容器更基本的概念。实际上,容器要常见得多。STL是在1996-1998年的时间压力下添加的,并且没有进行积极的重构。
您可以在std::for_each
的第三个参数中看到类似的问题。理论上,是存在的。在c++ 98中,它们没有。因此,您必须定义一个离线的函子,使用绑定器和组合器的详细语法,或者使用一个无状态的指向函数的指针。
我个人认为,所有STL算法都应该接受一个范围(单个对象),而不是一对迭代器。将后者包装在一个范围中是很简单的。现在您看到ostream_iterators必须定义一个相当任意的end对象。
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 如何在c++迭代器类型中包装std::chrono
- 集合上的输出迭代器:assign和increment迭代器
- Boost Spirit,获取迭代器内部语义动作
- 对于set上的循环-获取next元素迭代器
- 为什么output_editor Concept不需要output_e迭代器标记
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 使用迭代器时如何访问对象在向量中的位置?
- std::vector::迭代器是否可以合法地作为指针
- 跟随整数索引列表的自定义类迭代器
- 不明白迭代器,引用和指针失效,一个例子
- 我可以使用反向迭代器作为ForwardIt吗
- ESP8266单片机矢量迭代器的C++问题
- 如何在C++中将迭代器作为函数参数传递
- 是否应避免从非常量迭代器转换为常量迭代器?
- 如何在 c++ 中将字符串迭代器变量传递给函数?
- 为什么 vector 的随机访问迭代器给出与指针不同的内存地址?
- 为什么 C++ std::unordered_map 从 emplace/ 找到返回一个迭代器?