C++ 方法 ostream::exceptions(iostate state) 的行为应该是什么?

What is the behavior of the C++ method ostream::exceptions(iostate state) supposed to be?

提问人:Matthew Busche 提问时间:2/23/2019 最后编辑:Matthew Busche 更新时间:2/23/2019 访问量:262

问:

我正在编写自己的输出流库,并且我正在尝试模仿 std::ostream 行为,使其与我尝试实现的新行为不冲突。目前,我正在尝试模仿从ios继承的这个接口:

std::ostream::exceptions(ios::iostate state)

根据 cplusplus.com

“此方法为流设置新的异常掩码,并清除流的错误状态标志(就像调用了成员 clear() 一样)。”

我不清楚这是否意味着所有标志都会被清除,或者只在异常掩码中设置了标志,所以我编写了一个测试程序,但得到了非常出乎意料的结果。这是程序:

#include <sstream>
#include <iostream>

using namespace std;
int main(int argc, char** argv)
{
    ostringstream oss;
    try
    {
        cout << "            badbit           = " << ios::badbit        << "\n";
        cout << "            eofbit           = " << ios::eofbit        << "\n";
        cout << "            failbit          = " << ios::failbit       << "\n\n";

        cout << "            oss.rdstate()    = " << oss.rdstate()      << "\n";
        cout << "            oss.exceptions() = " << oss.exceptions()   << "\n\n";

        cout << "executing:  oss.setstate(ios::badbit | ios::failbit);" << "\n";
        oss.setstate(ios::badbit | ios::failbit);
        cout << "            oss.rdstate()    = " << oss.rdstate()      << "\n";
        cout << "            oss.exceptions() = " << oss.exceptions()   << "\n\n";

        cout << "executing:  oss.exceptions(ios::failbit);"             << "\n";
        oss.exceptions(ios::failbit);
        cout << "            oss.rdstate()    = " << oss.rdstate()      << "\n";
        cout << "            oss.exceptions() = " << oss.exceptions()   << "\n";
    }
    catch(const exception& x)
    {
        cout << endl;
        cout << "**** EXCEPTION THROWN ****"                            << "\n";
        cout << argv[0] << ":  " << x.what() << endl;
        cout << "            oss.rdstate()    = " << oss.rdstate()      << "\n";
        cout << "            oss.exceptions() = " << oss.exceptions()   << "\n";
        return 1;
    }
    catch(...)
    {
        cerr << argv[0] << ":  unknown exception." << endl;
        return 1;
    }
    return 0;
}

输出如下:

matt@dworkin:~/dev/ostream/libs/ostream$ g++ --version
g++ (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

matt@dworkin:~/dev/ostream/libs/ostream$ g++ --std=c++17 foo.cpp
matt@dworkin:~/dev/ostream/libs/ostream$ ./a.out

            badbit           = 1
            eofbit           = 2
            failbit          = 4

            oss.rdstate()    = 0
            oss.exceptions() = 0

executing:  oss.setstate(ios::badbit | ios::failbit);
            oss.rdstate()    = 5
            oss.exceptions() = 0

executing:  oss.exceptions(ios::failbit);

**** EXCEPTION THROWN ****
./a.out:  basic_ios::clear: iostream error
            oss.rdstate()    = 5
            oss.exceptions() = 4

因此,根据 cplusplus.com 文档,我根本没想到会抛出异常。从异常处理程序中生成的输出可以看出,从未清除过任何状态标志。那么这是编译器错误、文档错误,还是我遗漏了什么?

顺便说一句,我更喜欢所表现出的行为而不是记录的行为。对我来说似乎有点奇怪,抛出错误的请求会产生擦除现有错误的副作用。实际上,我首先以与 g++ 相同的方式实现了它(假设这是它肯定会工作的方式),然后才阅读此方法的文档。

C++ 异常 IOSTREAM ostream

评论

2赞 2/23/2019
“根据 cplusplus.com:” - 您正在尝试编写一个流库,并且您正在使用互联网上最蹩脚的资源之一来做到这一点?至少使用 cppreference.com。真的更好,忘掉它。
1赞 user4581301 2/23/2019
请求确认:您的问题是“如果设置后打开了流例外,为什么我会得到异常?failbitfailbit
0赞 Matthew Busche 2/23/2019
@Neil北海。也许“我自己的流库”是不准确的,因为它实际上只是一个抽象包装器,试图向某些现有的输出库(例如,ostream)添加新功能。但是我保留了自己的异常掩码和错误状态标志(因为我不想被绑定到 ostream 作为后盾)。但这一切都超出了我最初问题的范围。
0赞 Matthew Busche 2/23/2019
@user4581301:是的,这是我的问题之一(因为它似乎与 cplusplus.com 的说法相冲突)。此外,第二个问题是,cplusplus.com 说调用异常的副作用是调用 clear()(它应该清除错误状态标志),但没有清除任何标志。
1赞 user4581301 2/23/2019
C++ 标准说 clear 被称为。我会相信的。请注意,basic_ios::clear: iostream 错误表明是它引发了异常。这是我足够感兴趣的地方,可以查一下。检查链接并查看标准所说的内容。仍然在解开如何解释这在我的脑海中意味着什么。如果你在我之前管理它,请自行回答。clearclear

答:

1赞 user4581301 2/23/2019 #1

cplusplus.com 错了。它们是正确的,即 调用,但调用它不是为了清除状态,并且状态没有被清除。clearexceptions

尽管有这个名字,但 iostreams 在后端使用它,包括 setstate 本身,来完成大量设置的咕噜咕噜的工作,而不仅仅是清除流的状态。因此,它是容纳提出例外和其他与标志相关的行为的合乎逻辑的地方。clear

它似乎主要用于测试现有标志,以在已经设置任何所需标志时引发异常。若要在异常后实际清除标志,仍需要调用 to 以删除错误标志。exceptionsclearclear