请考虑以下代码:
std::exception_ptr eptr{ std::current_exception() };
const char* msg = 0;
try {
if (eptr != std::exception_ptr{}) {
std::rethrow_exception(eptr);
}
} catch(const std::exception& ex) {
msg = ex.what();
}
我可以在msg
之外使用catch
吗?换句话说,ex
引用与eptr
相同的异常实例?谢谢!
答案 0 :(得分:6)
rethrow_exception
的说明:
抛出:
p
引用的异常对象。
因此抛出的对象与指针所指的对象相同。指针可能会或可能不会引用原始的“当前异常”,因为current_exception
可能会复制异常对象,但这与您的问题无关。
所以是的,你重新抛出的异常对象与eptr
所拥有的异常对象相同,因此至少只要该指针拥有它就会保持活着。
答案 1 :(得分:3)
TL; DR :我不会,因为ex
和eptr
(可能?)引用了不同的例外情况。 (参见编辑:可能有保证,但标准不够明确,我会避免依赖它)
例外的生命周期由§15.1[except.throw] 决定,具体而言:
4 /异常对象的内存以未指定的方式分配,除非在§3.7.4.1中注明。如果处理程序通过重新抛出退出,则控制将传递给另一个处理程序以获取相同的异常。 异常对象在异常的最后剩余活动处理程序以除重新抛出之外的任何方式退出之后被销毁,或者引用异常对象的类型
std::exception_ptr
(第18.8.5节)的最后一个对象是在以前的情况下,破坏发生在处理程序退出时,在破坏处理程序中 exception-declaration 中声明的对象之后立即进行破坏(如果有的话)。在后一种情况下,破坏发生在std::exception_ptr
的析构函数返回之前。然后,实现可以为异常对象释放内存;任何这种解除分配都是以未指明的方式完成的。 [注意:抛出的异常不会传播到其他线程,除非使用适当的库函数捕获,存储和重新抛出;见§18.8.5和§30.6。 -end note ]
但是,您还需要考虑前一段:
3 /抛出异常copy-initializes(§8.5,§12.8)一个临时对象,称为异常对象。临时值是一个左值,用于初始化匹配处理程序中指定的变量(第15.3节)。 [...]
因此,在您的情况下会发生什么:
eptr
被初始化为指向当前飞行中的异常,保证此异常至少与它一样长。ex
已初始化为引用eptr
msg
指向副本,因此被悬空......除非副本和原件共享相同的消息。保证异常足够长的唯一方法是直接创建std::exception_ptr
。
编辑 Ralph Tandetzky引起我的注意,§18.8.5[传播] 说:
7 / [...] [注意:如果
rethrow_exception
重新抛出相同的异常对象(而不是副本),对该重新抛出的异常对象的并发访问可能会引入数据竞争。引用特定异常的exception_ptr
对象数量的变化不会引入数据 种族。 -end note ]
这表明在以下段落中:
不应将投掷子句解释为某种[[noreturn]] void rethrow_exception(exception_ptr p);
9 / 要求:
p
不得为空指针。10 / 抛出:
p
引用的异常对象。
throw *p;
,而是可以与throw;
具有相同的行为(投掷相同的实例,而不是副本)。但是,由于第7段中有 if ,它似乎也可能是副本......
注意:有趣的是实验表明the underlying object may be shared ......与我对标准的解读相矛盾;我不太明白 copy-initializes 如何以不同方式解释。