std::future 无 then() 因其设计为单次消费、不可拷贝的状态句柄,仅负责取结果;链式需手动封装或改用 std::packaged_task,否则编译失败或运行时崩溃。

std::future 没有 then(),强行链式会编译失败,必须手动封装或换用 std::packaged_task。
为什么 std::future::then() 不存在且不能直接调用
这是最常踩的坑:写 f1.then([](int x) { return x * 2; }) 直接报错 no member named 'then'。C++11/14/17/20 标准均未加入该接口——提案 P0159 已被搁置。根本原因在于 std::future 是单次消费、不可拷贝、无回调注册能力的状态句柄,它只负责“取结果”,不负责“触发后续”。所谓“链式”,本质是状态就绪后显式启动新任务,而非语法糖。
-
std::future移动后原对象valid()返回false,再调用get()抛std::future_error(no_state) -
get()阻塞且只能调用一次;在线程外无脑调用,整个链就退化成同步串行 - 试图把
std::future存入std::vector或按值捕获进 lambda,触发拷贝 → 编译失败(use of deleted function)
用 std::packaged_task 替代裸 std::promise/std::future 的实操要点
std::packaged_task 是更安全、更紧凑的起点:它把可调用体和其 std::future 绑定在一起,避免手动 new promise + set_value 的样板代码,且天然支持移动语义。
- 构造时签名必须严格匹配,例如
std::packaged_task,传入[](double) { ... }会编译失败 - 捕获局部变量时务必用值捕获(
[x]),而非引用([&x]);否则异步执行时主线程已退出,访问野内存 - 移动后原
std::packaged_task对象变为无效,再次调用会抛std::bad_function_call - 执行
task(42)后,关联的future状态自动变为 ready;未执行前调用wait()会阻塞
手动实现轻量 then() 扩展函数的关键约束
若坚持用 std::future 做链式中转,可用模板函数模拟 then 行为,但必须守住三条线:
- 参数
std::future必须以右值引用传入(std::future),防止意外拷贝&& - 内部必须用
std::thread(或线程池)执行,不能在当前栈帧里调用prev.get()—— 否则仍是阻塞 - 异常必须显式捕获并调用
p.set_exception(std::current_exception()),否则子线程异常会导致std::terminate - 返回的
std::future必须由新std::promise构造,绝不能复用上游 promise(重复set_value()抛promise_already_satisfied)
生命周期与异常传播中最容易被忽略的点
所有手动链式封装的崩溃,90% 出在两个地方:一个是 std::promise 提前析构,另一个是异常没传下去。
-
std::promise对象必须比其std::future的持有者活得更久;若在 lambda 中按值捕获std::promise,又没加mutable和std::move,set_value()会失败(no_state) - 上游任务抛异常,但下游只等
future.get(),不检查是否valid()或是否被set_exception(),就会在 get 时直接std::terminate - 用
std::thread::detach()启动 then 回调,子线程异常无法捕获;生产环境应改用std::async(std::launch::async, ...)或线程池包装
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











