c++std::ios_base::失败异常

c++ std::ios_base::failure exception

本文关键字:失败 异常 base ios c++std      更新时间:2023-10-16

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

类失败定义了所有对象类型的基类iostreams库中的函数作为异常抛出以报告在流缓冲区操作期间检测到错误。

我有一个简单的测试程序,它在使用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_alloc。其中一个在线编译器显示了相同的输出。

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

os << 'F';operator<<(ostream&, char),它是一个格式化输出函数,引用27.7.3.6.1[ostream.Formatted.rekmts],

该函数努力生成所请求的输出。如果生成失败,则格式化的输出函数执行setstate(ios_base::failbit),这可能会引发异常。如果在输出过程中引发异常,则ios::badbit将被打开,而不会引发ios::failure。处于*this的错误状态。如果是(exceptions()&badbit) != 0,则异常为重新引发

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

在libstdc++中,它将std::bad_allocstringbuf::overflow中抛出,CCD_17将堆栈一直展开到operator<<(从技术上讲,是__ostream_insert(,设置badbit,然后重新抛出(未修改(,如上所述。

在libc++中,std::bad_alloc被捕获在stringbuf::overflow中,它使overflow返回traits::eof,这反过来又使调用方(在本例中为steambuf::xsputn(返回零,这反过来使调用方__pad_and_output完全清除流的rdbuf,这反过来也使其调用方CCD27设置坏比特和故障比特。设置该坏比特会抛出您捕获的ios::failure

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

''返回:''traits::eof()表示失败。

除了分配失败之外,很难想象它会失败,但我认为libstdc++的解释更接近意图。(在libstdc++中,如果缓冲区容量达到string::max_size而不首先命中bad_alloc,则stringbuf::overflow仍然可以返回eof(

您正在创建的错误情况本身并不是由流缓冲区操作引起的错误。在某个时刻,您只是耗尽了内存,流的分配器将抛出一个bad_alloc。这是你看到的例外。

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