【问题标题】:Should std::ws raise failbit at end of file?std::ws 是否应该在文件末尾引发故障位?
【发布时间】:2012-11-16 19:56:33
【问题描述】:

是否应该使用 std::ws 操纵器从流中提取失败位? 在以下代码中,Clang 编译的(在 Xcode 4.5.1 中)程序无法通过最终断言。显然,EOF 处的 s >> std::ws 会导致失败。然而 GCC 4.7.2 通过了这个断言。哪个是正确的?

#include <iostream>
#include <sstream>
#include <cassert>

int main(int argc, const char * argv[])
{
    {
        // Read string with trailing ws.
        std::istringstream s( "test   " );
        std::string test;

        s >> std::ws;
        assert( !s.fail() );    // No ws to skip, but no failure.

        s >> test;
        assert( test == "test" );
        assert( !s.fail() );

        s >> std::ws;
        assert( !s.fail() );    // No prob skipping trailing ws.
    }
    {
        // Retry with no trailing ws.
        std::istringstream s( "test" );
        std::string test;

        s >> std::ws;
        assert( !s.fail() );    // No ws to skip, but no failure.

        s >> test;
        assert( test == "test" );
        assert( !s.fail() );

        s >> std::ws;
        assert( !s.fail() );    // CLANG: Skipping absent ws at eof raises failbit.
    }

    return 0;
}

【问题讨论】:

  • 对我来说 gcc 4.6、gcc 4.7 和 clang 3.1 都通过了每个断言

标签: c++ gcc clang iostream manipulators


【解决方案1】:

我相信 libc++ 正确地实现了标准。

[ 我引用的是 N3290,它是 C++11 标准草案。 C++14 不会改变这一点。 ]

ws 在 [istream.manip] 中有描述,其中指出:

效果:表现为未格式化的输入函数(如在 27.7.2.3,第 1 段),但不计算 提取的字符,不影响返回的值 随后调用 is.gcount()。构建哨兵对象后 只要下一个可用字符 c 是就提取字符 空格或直到序列中没有更多字符。 空白字符的区分标准与 由 sentry::sentry (27.7.2.1.3) 使用。如果 ws 停止提取字符 因为没有更多可用它设置 eofbit,但不设置失败位。

这里确定正确行为的关键短语是“在构造哨兵对象之后”。

Sentry 对象在 [istream::sentry] 中描述,其中的文本开始...

1 class sentry 定义了一个类,负责做异常安全的前缀和后缀操作。

显式哨兵(basic_istream& is, bool noskipws = false);

2 效果:如果 is.good() 为 false,则调用 is.setstate(failbit)。否则,准备 格式化或 > 未格式化的输入。 ...等等...

这是唯一可用的哨兵构造函数,所以这是 libc++ 使用的。

所以在提取任何字符之前会设置失败位,因此段落末尾的文本不适用。如果流包含" "(即末尾的单个空格),则调用std::ws 不会设置失败位,只会设置eof(这是OP 预期会发生的)。

【讨论】:

  • 对我来说,这似乎是对标准的公平解读,但由于它导致了一个不太有用的版本std::ws,我认为我们应该将 GCC 的解释视为正确的解释。 Clang 版本的问题在于它不能用于在输入末尾吃掉可选的空白。
  • 如果我们在实现之间存在分歧,那么我认为 C++ 标准委员会应该决定他们要指定的行为,并重新定义标准的该部分,以便清楚:- )
  • @HowardHinnant:Marshall 真的需要链接来了解如何创建 LWG 问题吗? ;-)
  • 不,我没有 :-) 但其他人可能会。
【解决方案2】:

C++11,§27.7.2.4/1:

如果ws 因为没有更多可用字符而停止提取字符,则设置eofbit,而不是failbit

因此,ws 操纵器不会直接设置 failbit。然而,正如 Marshall Clow 在他的回答中指出的那样,它不必——它需要创建一个哨兵对象,并且如果 !stream.good() 则需要哨兵对象设置故障位。

【讨论】:

  • 那么这是 libc++ 中的一个错误吗?
  • 根据 C++11 以来的 C++ 规范,这个答案是不正确的(我没有检查 C++03 规范)。
  • @HowardHinnant:是的——Marshall 应该真的得到“接受”标志,但只有 OP 可以不接受答案,直到/除非他接受,我不能删除它。
猜你喜欢
  • 2012-06-16
  • 2018-03-08
  • 1970-01-01
  • 1970-01-01
  • 2020-06-04
  • 2016-12-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多