c++ std::ios_base::failure 异常

c++ std::ios_base::failure exception

标准(N3337)表示(27.5.3.1.1 Class ios_base::failure):

The class failure defines the base class for the types of all objects thrown as exceptions, by functions in the iostreams library, to report errors detected during stream buffer operations.

我有一个简单的测试程序,它在使用 std::ostringstream:

时模拟受限资源环境
#include <sys/time.h>
#include <sys/resource.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <sstream>

int main(int argc, const char* argv[])
{
    rlimit limit;
    limit.rlim_cur = limit.rlim_max = 268435456;

    if(setrlimit(RLIMIT_AS, &limit)) {
        std::cerr << "Cannot set resource limit: " << strerror(errno) << std::endl;
        exit(EXIT_FAILURE);
    }

    std::ostringstream os;
    os.exceptions(std::ostringstream::badbit);

    try {
        auto iterations = 1024 * 1024 * 1024;

        while(iterations && --iterations) os << 'F';

    } catch(const std::ios_base::failure& ex) {
        std::cerr << "Caught: std::ios_base::failure" << std::endl;
    } catch(const std::bad_alloc& ex) {
        std::cerr << "Caught: std::bad_alloc" << std::endl;
    } catch(...) {
        std::cerr << "Caught: ellipsis" << std::endl;
    }

    return 0;
}

在我的环境中(Linux,gcc 5.3.0)我在 stderr 上得到了 Caught: std::bad_allocOne of online compilers 显示相同的输出。

问题是:为什么异常类型是std::bad_alloc而不是std::ios_base::failure

您正在创建的错误情况不是由流缓冲区操作本身引起的错误。在某些时候,您只是 运行 内存不足,流的分配器将抛出 bad_alloc。这是您看到的异常。

是否应该将 bad_alloc 作为 ios_base::failure 重新抛出是有争议的,因为最终流操作确实会失败。不过,在这种情况下看到 bad_alloc 情况我并不感到惊讶。

os << 'F';operator<<(ostream&, char),这是一个格式化输出函数,并且引用 27.7.3.6.1[ostream.formatted.reqmts],

the function endeavors to generate the requested output. If the generation fails, then the formatted output function does setstate(ios_base::failbit), which might throw an exception. If an exception is thrown during output, then ios::badbit is turned on without causing an ios::failure to be thrown. in *this’s error state. If (exceptions()&badbit) != 0 then the exception is rethrown

作为输出的一部分,此函数调用在 27.8.2.4[stringbuf.virtuals]p8 中指定的 stringbuf::overflow 来执行重新分配。这里libstdc++和libc++的区别是对其分配失败后果的解释:

在 libstdc++ 中,它从 stringbuf::overflow 中抛出 std::bad_alloc,将堆栈一直展开到 operator<<(技术上,__ostream_insert),设置 badbit 并重新抛出,未修改,如上所述。

在 libc++ 中,std::bad_alloc 被捕获在 stringbuf::overflow 中,它使 overflow return traits::eof,这反过来又使调用者 (在这种情况下,steambuf::xsputn) return 为零,这反过来又使调用者 __pad_and_output 完全清除流的 rdbuf,这又使其调用者 __put_character_sequence,同时设置 badbit 和 failbit。该 badbit 的设置会抛出您捕获的 ios::failure

也许 libc++ 在 stringbuf::overflow 中在技术上是正确的:标准说

''Returns:'' traits::eof() to indicate failure.

而且很难想象除了分配失败之外还有什么方法可以使它失败,但我认为 libstdc++ 的解释更接近本意。 (在libstdc++中,stringbuf::overflow仍然可以returneof,如果缓冲容量达到string::max_size而不先打bad_alloc