8

以下(玩具)程序在与 libstdc++ 和 libc++ 链接时返回不同的内容。这是 libc++ 中的错误还是我不明白 istream eof() 的工作原理?我尝试在 linux 和 mac os x 上使用 g++ 并在 mac os x 上使用 clang 运行它,有和没有 -std=c++0x。我的印象是 eof() 在尝试读取(通过 get() 或其他方式)实际上失败之前不会返回 true。这是 libstdc++ 的行为方式,但不是 libc++ 的行为方式。

#include <iostream>
#include <sstream>

int main() {
    std::stringstream s;

    s << "a";

    std::cout << "EOF? " << (s.eof() ? "T" : "F") << std::endl;
    std::cout << "get: " << s.get() << std::endl;
    std::cout << "EOF? " << (s.eof() ? "T" : "F") << std::endl;

return 0;
}

Thor:~$ g++ test.cpp
Thor:~$ ./a.out
EOF? F
get: 97
EOF? F
Thor:~$ clang++ -std=c++0x -stdlib=libstdc++ test.cpp 
Thor:~$ ./a.out
EOF? F
get: 97
EOF? F
Thor:~$ clang++ -std=c++0x -stdlib=libc++ test.cpp 
Thor:~$ ./a.out
EOF? F
get: 97
EOF? T
Thor:~$ clang++ -stdlib=libc++ test.cpp 
Thor:~$ ./a.out
EOF? F
get: 97
EOF? T
4

4 回答 4

5

编辑:这是由于旧版本的 libc++ 解释 C++ 标准的方式。该解释在LWG issue 2036中进行了讨论,被裁定为不正确,并更改了 libc++。

当前的 libc++ 在您的测试中给出与 libstdc++ 相同的结果。

旧答案:

你的理解是正确的。

istream::get()执行以下操作:

  1. 如果返回 false 则调用good(), 并设置(这会在设置了其他位的流中添加一个失败位), ( )failbit§27.7.2.1.2[istream::sentry]/2
  2. 如有必要,刷新任何 tie()'d
  3. 如果good()此时为假,则返回 eof 并且不执行任何其他操作。
  4. 就像通过调用rdbuf()->sbumpc()or rdbuf()->sgetc()( §27.7.2.1[istream]/2)一样提取字符
  5. 如果sbumpc()sgetc()返回 eof,则设置eofbit. ( §27.7.2.1[istream]/3) 和failbit( §27.7.2.2.3[istream.unformatted]/4)
  6. 如果抛出异常,则设置 badbit ( §27.7.2.2.3[istream.unformatted]/1) 并在允许时重新抛出。
  7. 更新 gcount 并返回字符(如果无法获得则返回 eof)。

(引用自 C++11 的章节,但 C++03 具有所有相同的规则,在 §27.6.* 下)

现在让我们看一下实现:

libc++(当前 svn 版本)将 get() 的相关部分定义为

sentry __s(*this, true);
if (__s)
{
    __r = this->rdbuf()->sbumpc();
    if (traits_type::eq_int_type(__r, traits_type::eof()))
       this->setstate(ios_base::failbit | ios_base::eofbit);
    else
        __gc_ = 1;
}

libstdc++(与 gcc 4.6.2 一起提供)定义与

sentry __cerb(*this, true);
if (__cerb)
  {
    __try
      {
        __c = this->rdbuf()->sbumpc();
        // 27.6.1.1 paragraph 3
        if (!traits_type::eq_int_type(__c, __eof))
          _M_gcount = 1;
        else
          __err |= ios_base::eofbit;
      }
[...]
if (!_M_gcount)
  __err |= ios_base::failbit;

如您所见,sbumpc()当且仅当 sbumpc() 返回 eof 时,两个库都会调用并设置 eofbit。

您的测试用例使用两个库的最新版本为我产生了相同的输出。

于 2012-01-25T18:03:37.047 回答
4

这是一个 libc++ 错误,已按照 Cubbi 的说明进行了修复。我的错。详情在这里:

http://lwg.github.io/issues/lwg-closed.html#2036

于 2012-01-26T15:14:10.433 回答
1

在第二次调用中未指定的值s.eof()——它可能是真或假,甚至可能不一致。您只能说,如果s.eof()返回 true,则所有未来的输入都将失败(但如果返回 false,则无法保证未来的输入会成功)。在失败 ( s.fail()) 之后,如果s.eof()返回 true,则很可能(但不是 100% 确定)失败是由于文件结尾。但是,值得考虑以下情况:

double test;
std::istringstream s1("");
s1 >> test;
std::cout << (s1.fail() ? "T" : "F") << (s1.eof() ? "T" : "F") << endl;
std::istringstream s2("1.e-");
s2 >> test;
std::cout << (s2.fail() ? "T" : "F") << (s2.eof() ? "T" : "F") << endl;

在我的机器上,两行都是"TT",尽管第一行失败是因为没有数据(文件结尾),第二行是因为浮点值的格式不正确。

于 2012-01-25T15:27:48.740 回答
0

当有一个操作试图读取文件末尾时设置 eofbit,该操作可能不会失败(如果您正在读取一个整数并且整数之后没有行尾,我希望设置 eofbit 但读取的整数成功)。IE 我得到并期待 FT

#include <iostream>
#include <sstream>

int main() {
    std::stringstream s("12");
    int i;
    s >> i;

    std::cout << (s.fail() ? "T" : "F") << (s.eof() ? "T" : "F") << std::endl;

    return 0;
}

在这里,我不希望 istream::get 在返回的字符之后尝试读取(即,如果我用它读取 \n,我不希望它挂起,直到我进入下一行),所以 libstd++ 似乎确实正确,至少在 QOI POV 中。

istream::get 的标准描述只是说“提取一个字符 c,如果可用的话”没有描述如何,因此似乎并没有阻止 libc++ 行为。

于 2012-01-25T15:27:40.643 回答