4

我将编译器从 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 上运行。这不是正确的行为吗?

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

  • compileonline声称是 gcc-4.7.2,它允许它,即使其他消息来源说行为在 4.6 中发生了变化。
  • stack-crooked, gcc-4.8 显示了新行为,并且 unsetf(skipws) 似乎没有效果。
  • 键盘允许它。分不清版本。

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

这些假设贯穿其中的代码体很大。

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

// 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;
}
4

1 回答 1

2

您似乎期望的行为可能是错误的。C++11 和 C++03 都tellg以“表现为未格式化的输入函数 [...]”开始描述。“未格式化的输入函数”从构造一个sentry对象开始,如果 sentry对象转换为false. 如果设置了,sentry对象将转换为。falseeofbit

该标准对于阅读数字是否设置 的规定略显不明确eofbit,但只是略为不明确(信息分布在几个不同的部分)。基本上,当输入一个数值时,流(实际上是num_getfacet)必须提前读取一个字符,以便知道数字在哪里结束。在您的情况下,它会在发生这种情况时看到文件结尾,因此将设置eofbit. 因此,您的第一个assert将因符合标准的实现而失败。

人们很容易认为这是标准中的缺陷,或者是无意的。很容易想象一些实现做了明智的事情(这似乎是您所期望的),可能是因为最初的实现者没有意识到标准中的全部含义(或者无意识地按照他们认为应该阅读的方式阅读了它)。我猜这就是 g++ 的情况,当他们意识到他们的行为不符合时,他们修复了它。

至于变通办法...我不确定真正的问题是什么,您正在尝试解决问题。但我认为,如果您在 之前清除错误位tellg,它应该可以工作。(当然,然后iScan.good()会是trueiScan.eof() false。但这真的有关系吗?)在清除状态之前一定要检查提取是否真的成功。

于 2013-08-28T14:46:02.943 回答