输入流上基于范围的循环 [英] Range-based loop over an input stream
问题描述
要遍历输入流,通常会像这样使用std::istream_iterator
:
To iterate over an input stream, we would usually use a std::istream_iterator
like so:
typedef std::istream_iterator<std::string> input_iterator;
std::ifstream file("myfile");
for (input_iterator i(file); i != input_iterator(); i++) {
// Here, *i denotes each element extracted from the file
}
如果我们可以使用基于范围的for
语句来迭代输入流,那就太好了.但是,对于类类型的对象,基于范围的for
要求对象具有begin()
和end()
成员函数(第6.5.4节,添加了粗体):
It'd be nice if we could use the range-based for
statement to iterate over input streams. However, for objects of class type, range-based for
requires the object to have begin()
and end()
member functions (§6.5.4, bold emphasis added):
如果
_RangeT
是数组类型,则 begin-expr 和 end-expr 分别是__range
和__range + __bound
,其中是数组绑定.如果 _RangeT
是未知大小的数组或类型不完整的数组,则程序格式错误;
if
_RangeT
is an array type, begin-expr and end-expr are__range
and__range + __bound
, respectively, where__bound
is the array bound. If_RangeT
is an array of unknown size or an array of incomplete type, the program is ill-formed;
如果_RangeT
是类类型,则在begin
和end
>好像按类成员访问查找(3.4.5),并且如果一个(或两个)都找到至少一个声明,则 begin-expr 和 end-expr 为__range.begin()
和__range.end()
分别;
if _RangeT
is a class type, the unqualified-ids begin
and end
are looked up in the scope of class _RangeT
as if by class member access lookup (3.4.5), and if either (or both) finds at least one declaration, begin-expr and end-expr are __range.begin()
and __range.end()
, respectively;
否则, begin-expr 和 end-expr 分别为begin(__range)
和end(__range)
,其中begin
和end
与参数相关的查找(3.4.2).出于此名称查找的目的,名称空间std
是关联的名称空间.
otherwise, begin-expr and end-expr are begin(__range)
and end(__range)
, respectively, where begin
and end
are looked up with argument-dependent lookup (3.4.2). For the purposes of this name lookup, namespace std
is an associated namespace.
输入流不具有这些成员函数(它们不是Container),因此基于范围的for
不适用于它们.无论如何,这是有道理的,因为您将需要某种方式来指定要提取的类型(在上述情况下为std::string
).
The input streams don't have these member functions (they are not Containers) and so range-based for
won't work on them. This makes sense anyway because you would need some way to specify the type to extract (std::string
in the case above).
但是,如果我们知道要提取的内容,是否可以为输入流定义我们自己的begin()
和end()
函数(也许是std::begin()
和std::end()
的特殊化或重载),以便如上所述由类成员访问查找找到?
But if we know what we want to extract, is it possible to define our own begin()
and end()
functions (perhaps specializations or overloads of std::begin()
and std::end()
) for input streams such that they would be found by class member access lookup as described above?
从6.5.4节开始(至少对我而言)尚不清楚,如果先前的查找失败,那么是否将使用依赖于参数的查找来查找函数.要考虑的另一件事是,std::ios_base
及其派生词已经有一个名为end
的成员,该成员是进行搜索的标志.
It's unclear (at least to me) from §6.5.4 whether the functions will then be looked up with argument-dependent lookup if the previous lookup fails. Another thing to consider is that std::ios_base
and its derivatives already have a member called end
which is a flag for seeking.
这是预期的结果:
std::ifstream file("myfile");
for (const std::string& str : file) {
// Here, str denotes each element extracted from the file
}
或者:
std::ifstream file("myfile");
for (auto i = begin(file); i != end(file); i++) {
// Here, *i denotes each element extracted from the file
}
推荐答案
一种显而易见的方法是为流使用简单的装饰器,以提供类型和必要的接口.这是这样的样子:
An obvious approach is to use a simple decorator for your stream providing the type and the necessary interface. Here is how this could look like:
template <typename T>
struct irange
{
irange(std::istream& in): d_in(in) {}
std::istream& d_in;
};
template <typename T>
std::istream_iterator<T> begin(irange<T> r) {
return std::istream_iterator<T>(r.d_in);
}
template <typename T>
std::istream_iterator<T> end(irange<T>) {
return std::istream_iterator<T>();
}
for (auto const& x: irange<std::string>(std::ifstream("file") >> std::skipws)) {
...
}
这篇关于输入流上基于范围的循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!