简体   繁体   English

eof上的C ++ istream tellg()/ fail():行为更改; 变通?

[英]C++ istream tellg()/fail() on eof: behavior change; work-around?

I upgraded my compiler from gcc-4.4 to gcc-4.8 and one project fails miserably stemming from the following (false) assumptions: 我将编译器从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;
}

On gcc-4.4, relevant assertions pass. 在gcc-4.4上,相关断言通过。 On gcc-4.8, tellg() returns -1 and and fail() returns !false, apparently since it hit eof. 在gcc-4.8上,tellg()返回-1,而fail()返回!false,这显然是因为它命中了eof。

My target is MinGW 32-bit as shipped with Qt 5.1 (gcc-4.8). 我的目标是Qt 5.1(gcc-4.8)附带的MinGW 32位。

Questions: 问题:

  • Is the old behavior really in error, per N3168 or other? 根据N3168或其他方式,旧行为是否真的有错误? (Which other?) (哪个?)
  • Is there a global, reliable, language-independent work-around? 是否存在一种全局的,可靠的,独立于语言的解决方法? (I'm guessing not.) (我猜不是。)
  • Is there a global, reliable, gcc work-around that spans versions? 是否存在跨版本的全局,可靠的gcc解决方法?
  • Even when I do the above unsetf(skipws), it still doesn't work on gcc-4.8. 即使当我执行上述unsetf(skipws)时,它在gcc-4.8上仍然不起作用。 Isn't that incorrect behavior? 不是不正确的行为?

Further, various online compilers give different behavior. 此外,各种在线编译器会给出不同的行为。 Is that a function of their lib? 这是他们的lib的功能吗?

  • compileonline , which claims to be gcc-4.7.2, allows it even though other sources say behavior changed in 4.6. compileonline声称是gcc-4.7.2,即使其他消息来源称行为在4.6中发生了变化,它也允许它。
  • stack-crooked , gcc-4.8, shows the new behavior, and unsetf(skipws) seems to have no effect. stack-crooked gcc-4.8显示了新行为,而unsetf(skipws)似乎没有任何作用。
  • codepad allows it. 键盘允许它。 Can't tell version. 无法分辨版本。

Other similar but not duplicate questions: 其他类似但不重复的问题:

The body of code, with these assumptions running through it all, is large. 假设所有这些假设都贯穿其中,代码主体很大。

Update: Here's a key part of the answer, which should work across all versions, all compilers: 更新:这是答案的关键部分,它应适用于所有版本,所有编译器:

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

The behavior you seem to expect is probably wrong. 您似乎期望的行为可能是错误的。 Both C++11 and C++03 start the description of tellg with "Behaves as an unformatted input function[...]". C ++ 11和C ++ 03 tellg “作为无格式的输入函数[...]表现”开始对tellg进行描述。 An "unformatted input function" starts by constructing a sentry object, and will fail, doing nothing and returning a failure status, if the sentry object converts to false . “未格式化的输入函数”通过构造sentry对象开始,并且如果sentry对象转换为false ,则将失败,不执行任何操作并返回失败状态。 And the sentry object will convert to false if the eofbit is set. 如果设置了eofbitsentry对象将转换为false

The standard is slightly less clear about whether reading the number sets the eofbit , but only slightly (with the information spread out over several different sections). 该标准对于读取数字是否设置eofbit不太清楚,但只是略微(信息分散在几个不同的部分)。 Basically, when inputting a numeric value, the stream (actually, the num_get facet) must read one character ahead, in order to know where the number ends. 基本上,输入数字值时,流(实际上是num_get构面)必须提前读取一个字符,以便知道数字的结尾。 In your case, it will see the end of file when this occurs, and will thus set eofbit . 在您的情况下,它会在发生这种情况时看到文件的结尾,从而设置eofbit So your first assert will fail with a conforming implementation. 因此,您的第一个assert将因符合标准的实现而失败。

One could very easily consider this a defect in the standard, or unintentional. 可以很容易地认为这是标准的缺陷,或者是无意的。 It's very easy to imagine some implementations doing the sensible thing (which is what you seem to expect), perhaps because the original implementors didn't realize the full implications in the standard (or unconsciously read it as they thought it should read). 很难想象某些实现会做出明智的事情(这似乎是您所期望的),这可能是因为原始实现者没有意识到标准的全部含义(或者不自觉地阅读了标准的含义)。 I would guess that this is the case for g++, and when they realized that their behavior was non-conformant, they fixed it. 我想这是g ++的情况,当他们意识到自己的行为不符合要求时,便将其修复。

As for work-arounds... I'm not sure what the real problem is, that you're trying to work around. 至于解决方法...我不确定真正的问题是,您正在尝试解决。 But I think that if you clear the error bits before the tellg , it should work. 但是我认为,如果您在tellg之前清除错误位,它应该可以工作。 (Of course, then iScan.good() will be true , and iScan.eof() false . But can this really matter?) Just be sure to check that the extraction actually succeeded before you clear the status. (当然,然后iScan.good()将为true ,而iScan.eof() false 。但这真的很重要吗?)在清除状态之前,请务必确保检查提取是否成功。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM