【发布时间】:2012-10-13 19:13:27
【问题描述】:
要遍历输入流,我们通常会像这样使用std::istream_iterator:
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 节,加粗强调):
如果
_RangeT是一个数组类型,begin-expr和end-expr分别是__range和__range + __bound,其中__bound是数组绑定。如果_RangeT是一个未知大小的数组或一个不完整类型的数组,则程序是非良构的;如果
_RangeT是一个类类型,unqualified-idsbegin和end在类_RangeT的范围内查找,好像通过类成员访问查找 (3.4.5),如果任一(或两者)找到至少一个声明,begin-expr 和 end-expr 为__range.begin()和分别为__range.end();否则,begin-expr 和 end-expr 分别是
begin(__range)和end(__range),其中begin和end用参数查找依赖查找(3.4.2)。出于此名称查找的目的,命名空间std是一个关联的命名空间。
输入流没有这些成员函数(它们不是容器),因此基于范围的for 无法对它们起作用。无论如何,这是有道理的,因为您需要某种方式来指定要提取的类型(在上面的例子中为std::string)。
但是,如果我们知道我们想要提取什么,是否可以为输入流定义我们自己的 begin() 和 end() 函数(可能是 std::begin() 和 std::end() 的特化或重载),这样它们将是如上所述通过类成员访问查找找到?
如果先前的查找失败,则从 §6.5.4 中不清楚(至少对我而言)是否会使用依赖于参数的查找来查找函数。另一件需要考虑的事情是std::ios_base 及其派生词已经有一个名为end 的成员,这是一个用于寻找的标志。
这是预期的结果:
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
}
【问题讨论】:
-
只是我,还是从规范中看不清楚?似乎
std::begin()和std::end()只有在_RangeT不是数组或类类型时才能找到。 -
是的,这不是最好的措辞,但我认为您应该将其理解为“如果它是一个类并且它具有 .begin 和 .end 那么它将使用那些......否则”,即你可以提供免费的功能。
-
"
begin和end在类 _RangeT ... 的范围内查找,如果 其中一个 ... 找到至少一个声明,begin-expr和end-expr是__range.begin()和__range.end()" - 因为std::ios_base::end确实存在(因此会找到std::ifstream::end)游戏结束。.begin()将找不到,.end()将是语法错误。 -
FWIW Boost.Range 提供
istream_range。 Demo.
标签: c++ c++11 iterator inputstream