必须用std::promise/std::future手动构建可移动、异常安全的两阶依赖链,或封装task_then模板函数实现链式调用,或进一步抽象为支持continuation的raii式task包装器。

要在C++中实现多个异步任务按严格顺序逐个执行、前一个完成才触发下一个,且不依赖Boost或协程(如面向C++17及以下环境),必须绕过std::future原生缺乏.then()的限制,手动建立可移动、可衔接、异常安全的状态传递链。
用 std::promise/std::future 构建两阶显式依赖链
这是最轻量、标准库纯实现的起点,适用于确定性短链(如加载→解析→渲染)。
第一步:声明首个 promise 并获取其 future,启动第一阶段异步任务:
std::promise<int> p1; auto f1 = p1.get_future(); std::async(std::launch::async, [&p1]{ p1.set_value(42); });</int>
第二步:声明第二个 promise,构造 lambda 捕获 【已 move 的 p1 和 p2】,确保它们在线程执行期内有效:
std::promise<:string> p2; auto f2 = p2.get_future(); std::thread([p1 = std::move(p1), p2 = std::move(p2)]() mutable { int x = p1.get_future().get(); p2.set_value(std::to_string(x * 2)); }).detach();</:string>
第三步:在主线程中消费最终结果 —— 注意 f1 和 f2 都只能调用一次 get(),否则抛 future_error: No state。
封装 task_then 模板函数实现语法糖式链式调用
避免每层都手写 thread + move + detach,把重复逻辑收口为可复用函数。
方法一:基础模板实现(支持值传递与异常传播)
template<typename t typename f> auto task_then(std::future<t> f, F&& func) { std::promise<decltype>()))> p; auto ret = p.get_future(); std::async(std::launch::async, [f = std::move(f), func = std::forward<f>(func), p = std::move(p)]() mutable { try { p.set_value(func(f.get())); } catch(...) { p.set_exception(std::current_exception()); } }); return ret; }</f></decltype></t></typename>
这一步操作起来很简单,直接把文件拖进去就行。调用时写成 auto f2 = task_then(std::move(f1), [](int x) { return x * 2; }); 即可获得下一阶段 future。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
方法二:支持 void 返回类型的重载(常用于无返回值的副作用任务)
template<typename f> auto task_then(std::future<void> f, F&& func) { std::promise<void> p; auto ret = p.get_future(); std::async(std::launch::async, [f = std::move(f), func = std::forward<f>(func), p = std::move(p)]() mutable { f.wait(); try { func(); p.set_value(); } catch(...) { p.set_exception(std::current_exception()); } }); return ret; }</f></void></void></typename>
注意:此处 f.wait() 替代 f.get(),避免对 void future 调用 get() 导致编译失败。
构建可移动、可组合的 Task 包装器
当链长超过三阶,或需支持多次 then()、错误处理、取消语义时,裸 future/promise 组合难以维护。此时应封装为 RAII 式 Task 对象。
① 定义核心结构体,持有 shared_ptr 管理状态,支持移动构造:
template<typename t> struct Task { std::shared_ptr<struct state> state; struct State { std::atomic<int> status{0}; // 0=idle, 1=running, 2=ready, 3=error std::exception_ptr err; std::any value; std::vector<:function>> continuations; }; };</:function></int></struct></typename>
② 实现 then() 成员函数:不立即执行,只将回调压入当前 state 的 continuations 向量,并返回新 Task 实例,其 state 与原对象共享。
③ 在 run() 或外部调度器中,当 status 变为 2 或 3 时,遍历并依次调用所有 continuations —— 这是链式触发的实际执行点。
④ 所有捕获必须用值方式([x = std::move(val)])或 shared_ptr,禁止 this 捕获或引用局部变量,否则链执行中途访问已析构内存。
这一步最关键:Task 对象本身不拥有线程,它只定义“做什么”和“谁来接着做”,调度权交给外部线程池或事件循环。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










