C ++ istream tellg()/ fail()关于eof:行为改变;变通?

时间:2013-08-28 14:19:42

标签: c++ qt gcc istream

我将编译器从gcc-4.4升级到gcc-4.8,并且一个项目因以下(错误)假设而失败:

#include <sstream>
#include <assert.h>

int main()
{
    using namespace std;
    istringstream iScan;
    int num;

    //iScan.unsetf(std::ios::skipws);
    iScan.str("5678");
    iScan >> num;
    assert(iScan.tellg() == istringstream::pos_type(4));
    assert(!iScan.fail());
    assert(!iScan.good());
    assert(iScan.eof());
    assert(num == 5678);
    assert(false && "We passed the above assertions.");
    return 0;
}

在gcc-4.4上,相关的断言通过。在gcc-4.8上,tellg()返回-1并且fail()返回!false,显然是因为它命中了eof。

我的目标是Qt 5.1(gcc-4.8)附带的MinGW 32位。

问题:

  • 根据N3168或其他情况,旧行为是否真的出错? (还有哪个?)
  • 是否存在全球性,可靠,与语言无关的解决方案? (我猜不是。)
  • 是否有跨越版本的全球性,可靠的gcc解决方案?
  • 即使我执行上述unsetf(skipws),它仍然无法在gcc-4.8上运行。是不是 不正确的行为?

此外,各种在线编译器提供不同的行为。这是他们的lib的功能吗?

    声称是gcc-4.7.2的
  • compileonline允许它,即使其他来源说4.6中的行为发生了变化。
  • stack-crooked,gcc-4.8,显示新行为,而unsetf(skipws)似乎没有效果。
  • codepad允许。无法告诉版本。

其他类似但不重复的问题:

这些假设贯穿全部的代码体很大。

更新:这是答案的关键部分,应该适用于所有版本,所有编译器:

// istream::tellg() is unreliable at eof(): works w/gcc-4.4, doesn't w/gcc-4.8.
#include <sstream>
#include <assert.h>

using namespace std;
typedef istream::pos_type   pos_type;

pos_type reliable_tellg(istream &iScan)
    {
    bool wasEOF = iScan.eof();
    if (wasEOF)
        iScan.clear(iScan.rdstate() & ~ios::eofbit); // so tellg() works.
    pos_type r = iScan.tellg();
    if (wasEOF)
        iScan.clear(iScan.rdstate() | ios::eofbit); // restore it.
    return r;
    }


int main()
{
    istringstream iScan;
    int num, n2;

    //iScan.unsetf(std::ios::skipws);
    iScan.str("5678");
    assert(!iScan.eof() && !iScan.fail()); // pre-conditions.
    assert(reliable_tellg(iScan) == pos_type(0));

    iScan >> num;
    assert(!iScan.fail());
    assert(reliable_tellg(iScan) == pos_type(4));
    assert(iScan.eof());
    assert(reliable_tellg(iScan) == pos_type(4)); // previous calls don't bungle it.
    assert(num == 5678);

    iScan >> n2; // at eof(), so this should fail.
    assert(iScan.fail());
    assert(reliable_tellg(iScan) == pos_type(-1)); // as expected on fail()
    assert(iScan.eof());

    assert(false && "We passed the above assertions.");
    return 0;
}

1 个答案:

答案 0 :(得分:1)

您似乎期望的行为可能是错误的。两者都是C ++ 11 和C ++ 03用{Behaves as a}开始tellg的描述 无格式输入功能[...]“。”无格式输入 函数“从构造sentry对象开始,并且将会 失败,什么也不做,并返回失败状态,如果 sentry对象转换为false。和sentry对象 如果false已设置,则会转换为eofbit

关于是否阅读,标准略显不清楚 number设置eofbit,但只是稍微设置{ 信息分散在几个不同的部分)。 基本上,当输入数值时,流(实际上, num_get facet)必须提前读取一个字符,以便 知道号码的终点。在你的情况下,它将看到结束 发生这种情况时的文件,因此将设置eofbit。所以你的 第一个assert将失败,并且符合实施。

人们可以很容易地认为这是标准中的缺陷,或者 无意的。一些实现很容易想象 做出明智的事(这是你所期待的), 也许是因为原来的实现者没有意识到 标准中的全部含义(或无意识地将其视为 他们认为应该阅读)。我猜这就是 g ++的情况,当他们意识到他们的行为是 不符合要求,他们修好了。

至于解决方案......我不确定真正的问题是什么, 你正试图解决这个问题。但我想如果你 清除tellg之前的错误位,它应该工作。 (的 当然,iScan.good()将是trueiScan.eof() false。但这真的很重要吗?)一定要检查 在你清除之前,提取实际上已成功 状态。