那么,runtime_error::runtime_error如何满足throw()呢?
(为了背景,我正在实现一个异常类型,并想从调用站点存储几个std::string,并希望正确地执行。)
(这里有一个相同的最小测试用例。)
runtime_error::runtime_error(string const&)
不需要满足throw()
。
它不继承或覆盖exception::exception()
,并且在调用string
的复制构造函数时,exception::exception()
已经完成。
如果复制string
引发异常,则会解开runtime_error::runtime_error(string const&)
,然后可能调用exception::~exception()
。
[2003: 15.4/3]
如果虚函数有异常说明,那么任何派生类中覆盖该虚函数的所有声明(包括定义)只允许基类虚函数异常说明所允许的异常。
但显然,exception::exception()
不是虚函数,而且runtime_error::runtime_error(string const&)
也没有重写它。
(请注意,这种情况适用于虚析构函数;因此,您可以看到,在libstdc++中,runtime_error::~runtime_error()
是throw()
。)
std::exception
必须是 nothrow
的,但是从 std::exception
派生的类可能会在它们的构造函数中抛出异常? - Billy ONealnothrow
;接受字符串的构造函数则不应该是。看起来你正在使用默认构造函数的签名来引用所有构造函数? - Potatoswatter然而,
std::runtime_error
接受一个std::string
作为其参数,这表明它在某个地方存储了一个std::string
。因此,必须进行赋值或复制构造。对于std::string
,这不是一个noexcept
操作。
runtime_error
(和logic_error
)只需要接受一个类型为std::string const &
的参数。它们不需要复制它。
使用这些重载要自行承担风险。LLVM libc++不提供存储空间。
另一方面,GNU libstdc++小心翼翼地避免内存不足。它将字符串的内容复制到异常存储空间中,而不是到一个新的std::string
中。
即使如此,它也添加了一个std::string&&
重载,并使用friend
关系来采用通过rvalue传递的std::string
参数的内部缓冲区,以节省异常存储空间。
所以这就是你真正的答案:“如果有的话,那就非常小心。”
你可以利用GCC的慷慨,使用std::runtime_error
作为自己异常类的成员,每个存储一个字符串。但是在Clang上这仍然无用。
堆栈展开期间发生异常会导致调用terminate
。
但是抛出对象的构造不是展开的一部分,并且与throw
表达式之前的代码没有区别。
如果std::runtime_error::runtime_error(std::string const &)
引发std::bad_alloc
,则runtime_error
异常将丢失(它从未存在),并且将处理bad_alloc
。
至于存储来自调用点的std::string
的您自己的类,您需要遵循§18.8.1/2:
派生自类异常的每个标准库类T都必须具有公共可访问的复制构造函数和公共可访问的复制赋值运算符,它们不会以异常退出。
这是必需的,因为从堆栈复制到线程的异常存储对异常很敏感。§15.1/7:
如果异常处理机制在完成评估要抛出的表达式之后,但在捕获异常之前调用一个通过异常退出的函数,则会调用std::terminate(15.5.1)。
runtime_error
构造函数之前,string
会被默认构造。 - Billy ONealchar
数组,其大小为sizeof(std::string)
,然后将字符串放置到该数组中,通过捕获异常来实现。这样可以在不需要使用库实现细节的情况下检测string
构造函数是否会抛出异常。 - bdonlan