不使用std::current_exception为std::promise设置异常的可行方案
C++ promise直接设置指定异常的实现方案
完全可以直接给set_exception传入指定异常,根本不需要走「先抛出再捕获拿std::current_exception」的繁琐流程——其实你贴的参考示例的注释里已经给出了方案,就是用标准库自带的std::make_exception_ptr生成异常指针传入即可,你担心的生命周期问题、内存泄漏问题在标准用法下都不存在。
核心原理
std::exception_ptr本身带引用计数托管能力,只要是通过标准库合规接口生成的exception_ptr,会自动管理持有的异常对象生命周期:最后一个指向该异常的exception_ptr销毁时,异常对象会自动被回收,既不会因为局部对象出作用域销毁出现悬垂指针,也不会有手动分配内存导致的泄漏。
你担心的问题只会出现在手动new异常、拿局部异常裸指针强转这类错误用法里,走标准接口完全没这个问题。
简化后的代码实现
原示例里两层try-catch的冗余逻辑可以大幅精简,不需要为了拿异常指针主动触发异常抛出:
std::thread t([&p]{ try { // 直接构造异常并生成exception_ptr传入,无需throw-catch流程 p.set_exception(std::make_exception_ptr(std::runtime_error("Example"))); // 自定义异常用法完全一致 // p.set_exception(std::make_exception_ptr(MyException("mine"))); } catch(...) { // 这里仅需要捕获set_exception本身可能抛出的异常 // 比如promise已经设置过值/异常时会抛出std::future_error } });
注意事项
std::make_exception_ptr会自动把传入的异常对象拷贝/移动(支持移动语义的类型会优先移动,开销很低)到自己管控的内存空间,和原局部对象的生命周期完全解绑,不会出现悬垂问题。- 这种写法比throw再捕获的流程性能更好,省掉了异常抛出、栈展开的额外开销,C++11及之后的标准版本都原生支持,可放心使用。
- 不要尝试手动new异常对象、取局部异常变量的地址强转为
exception_ptr,这类非标准用法才会触发你担心的生命周期、内存泄漏问题。
内容的提问来源于stack exchange,提问作者Lorah Attkins
相关产品推荐
相关产品推荐

