C++11/14/17 的 std::future 无 then(),因设计上拒绝隐式回调链;需手动用 std::promise + std::async 桥接,严格管理移动、异常和生命周期,否则易崩溃或阻塞。

标准 C++11/14/17 的 std::future 没有 then() 方法,硬写 f1.get().then(...) 会编译失败或运行时阻塞——这不是语法糖缺失的问题,而是设计上就拒绝隐式回调链。真要链式,必须自己桥接 std::promise 和 std::future,且每一步都得手动管理移动、异常、生命周期。
为什么 std::future::get() 不能直接用于链式启动
因为 get() 是阻塞调用,且只能调用一次。你在构造下一个任务时就调用它(比如 std::async([f]{ return f.get() + 1; })),等于把异步逻辑退化成同步串行,主线程卡在等待上,彻底失去“异步链”意义。
- 第二次调用
f.get()抛std::future_error:no state - lambda 捕获
std::future时若用引用,线程内访问可能读到已析构对象 - 不显式
std::move(f)捕获,编译失败:std::future不可拷贝 - 漏处理异常时,下游
get()会 rethrow,但没地方 catch —— 子线程崩溃静默
用 std::promise + std::thread 实现安全链式节点
核心是:每个阶段创建新 std::promise,前一阶段完成时调用其 set_value() 或 set_exception();所有捕获必须是值捕获 + std::move,lambda 声明为 mutable。
- 不要用
std::thread(...).detach()直接裸调,容易因栈变量提前销毁导致 crash;改用std::async(std::launch::async, ...)更可控 -
std::promise对象必须在线程执行期间持续有效 —— 推荐在 lambda 内以值捕获并std::move进去 - 返回的
std::future可安全 move 出函数,它内部持有共享状态句柄,不依赖原始std::promise是否还在作用域 - 示例节选:
template<typename T, typename F> auto then(std::future<T> f, F&& func) -> std::future<decltype(func(std::declval<T>()))> { std::promise<decltype(func(std::declval<T>()))> p; auto ret = p.get_future(); std::async(std::launch::async, [f = std::move(f), func = std::move(func), p = std::move(p)]() mutable { try { p.set_value(func(f.get())); } catch (...) { p.set_exception(std::current_exception()); } }); return ret; }
C++20 协程方案不是“更简单”,而是换了一套约束
协程能写出 co_await f1; auto x = co_await f2; 这种线性代码,但前提是你要先让 std::future 可被 co_await —— 得特化 operator co_await,返回自定义 awaitable 对象,并正确实现 await_ready/await_suspend/await_resume。否则照样编译不过。
立即学习“C++免费学习笔记(深入)”;
- 协程函数返回类型不能是裸
std::future,得封装成task<t></t>或类似可等待类型 -
co_await一个已就绪的std::future不会挂起,但若 future 尚未 ready,协程会 suspend 并交由调度器 resume —— 你得自己提供调度器逻辑 - 没有线程池或事件循环支撑时,协程只是语法糖,底层仍需
std::thread或std::jthread驱动 - 生产环境慎用
std::executionTS,目前主流编译器支持不一致
最容易被忽略的三个点
一是 std::future 的移动语义必须贯穿全程:从 std::async 返回、到 lambda 捕获、再到传给下一级 then(),任何一处忘了 std::move 就编译报错;二是异常传播不能只靠 try/catch,必须显式调用 p.set_exception(std::current_exception()),否则下游 get() 会 terminate;三是所有链式节点的 std::promise 生命周期必须严格覆盖其 set_* 调用时机 —— 用局部变量 + 值捕获是最稳妥方式,别试图用指针或引用延长它的命。


















