我已经使用<<
流运算符为对象实现了反序列化例程。例程本身使用一个istreambuf_iterator<char>
从流中一个一个地提取字符,以构造对象。
最终,我的目标是能够使用 an 遍历流istream_iterator<MyObject>
并将每个对象插入到vector
. 非常标准,除了当它到达流尾时我无法istream_iterator
停止迭代。现在,它只是永远循环,即使调用istream::tellg()
表明我在文件末尾。
这是重现问题的代码:
struct Foo
{
Foo() { }
Foo(char a_, char b_) : a(a_), b(b_) { }
char a;
char b;
};
// Output stream operator
std::ostream& operator << (std::ostream& os, const Foo& f)
{
os << f.a << f.b;
return os;
}
// Input stream operator
std::istream& operator >> (std::istream& is, Foo& f)
{
if (is.good())
{
std::istreambuf_iterator<char> it(is);
std::istreambuf_iterator<char> end;
if (it != end) {
f.a = *it++;
f.b = *it++;
}
}
return is;
}
int main()
{
{
std::ofstream ofs("foo.txt");
ofs << Foo('a', 'b') << Foo('c', 'd');
}
std::ifstream ifs("foo.txt");
std::istream_iterator<Foo> it(ifs);
std::istream_iterator<Foo> end;
for (; it != end; ++it) cout << *it << endl; // iterates infinitely
}
我知道在这个简单的例子中我什至不需要 istreambuf_iterator,但我只是想简化问题,以便人们更有可能回答我的问题。
所以这里的问题是,即使istreambuf_iterator
到达流缓冲区的末尾,实际的流本身也不会进入EOF
状态。调用istream::eof()
返回 false,即使istream::tellg()
返回文件中的最后一个字节,istreambuf_iterator<char>(ifs)
并将 true与 进行比较istreambuf_iterator<char>()
,这意味着我肯定在流的末尾。
我查看了 IOstreams 库代码,以确切了解它如何确定 anistream_iterator
是否位于结束位置,并且基本上它检查是否istream::operator void*() const
评估为true
. 这个 istream 库函数只返回:
return this->fail() ? 0 : const_cast<basic_ios*>(this);
换句话说,0
如果设置了故障位,则返回 (false)。然后它将这个值与默认构造的实例中的相同值进行比较,istream_iterator
以确定我们是否在最后。
因此,当与结束迭代器比较为真时,我尝试在std::istream& operator >> (std::istream& is, Foo& f)
例程中手动设置故障位。istreambuf_iterator
这完美地工作,并正确终止了循环。但现在我真的很困惑。似乎istream_iterator
肯定会检查std::ios::failbit
以表示“流结束”条件。但这不std::ios::eofbit
就是为了什么吗?我认为failbit
是针对错误情况,例如,如果fstream
无法打开 an 的基础文件或其他情况。
那么,为什么我需要调用istream::setstate(std::ios::failbit)
来终止循环呢?