Bil*_*eal 17 c++ exception-specification
std::exception要求它的构造函数是throw().然而std::runtime_error接受一个std::string作为它的论据,这表明它存储在std::string某个地方.因此,必须在某处进行分配或复制构造.而且std::string,这不是一项nothrow行动.
怎么会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().
很难直接表明派生的ctor不需要满足基本ctor的异常说明符,但是下面的段落(它描述了如何调用base的析构函数,而不是将异常传递给基础构造函数)强烈暗示了这一点. ):
[2003: 15.2/2]部分构造或部分销毁的对象将为其所有完全构造的子对象执行析构函数,即对于构造函数已完成执行且析构函数尚未开始执行的子对象.如果自动数组元素的构造函数抛出异常,则只会破坏该数组的构造元素.如果在new-expression中分配了对象或数组,则调用匹配的释放函数(3.7.3.2,5.3.4,12.5)(如果有)以释放对象占用的存储空间.
唯一接近你假设的场景(我最初推测的那段)的通道如下.
[2003: 15.4/3]如果虚函数具有异常规范,则在任何派生类中覆盖该虚函数的任何函数的所有声明(包括定义)都应仅允许基类虚函数的异常规范所允许的异常.
但显然exception::exception()不是虚函数,显然runtime_error::runtime_error(string const&)不会覆盖它.
(请注意,此方案将适用于虚析构函数;因此,你可以看到,在的libstdc ++,runtime_error::~runtime_error()是throw()).
然而
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&&重载并使用friendship来采用std::stringrvalue传递的参数的内部缓冲区,以节省异常存储空间.
这就是你的真实答案:"非常小心,如果有的话."
您可以通过使用std::runtime_errors作为您自己的异常类的成员来利用GCC的慷慨,每个都存储一个字符串.不过,对于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).
因此,您应该在第一次之后使用一个shared_ptr< std::string >或几个来清理副本.