9

使用 std::ws 操纵器从流中提取是否应该提高失败位?在以下代码中,Clang 编译的(在 Xcode 4.5.1 中)程序无法通过最终断言。显然s >> std::ws在 EOF 会导致失败。然而 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;
}
4

2 回答 2

4

我相信 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 类哨兵定义了一个类,负责做异常安全的前缀和后缀操作。

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

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

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

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

于 2014-04-21T16:24:23.540 回答
3

C++11,§27.7.2.4/1:

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

所以,ws机械手不failbit直接设置。但是,正如 Marshall Clow 在他的回答中指出的那样,它不必 - 它需要创建一个哨兵对象,并且哨兵对象需要设置故障位 if !stream.good()

于 2012-11-16T20:18:05.260 回答