必须用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>
立即学习“C++免费学习笔记(深入)”;
第二步:声明第二个 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(func(std::declval<T>()))> 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; }
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
这一步操作起来很简单,直接把文件拖进去就行。调用时写成 auto f2 = task_then(std::move(f1), [](int x) { return x * 2; }); 即可获得下一阶段 future。
方法二:支持 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.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<std::function<void()>> continuations; }; };
② 实现 then() 成员函数:不立即执行,只将回调压入当前 state 的 continuations 向量,并返回新 Task 实例,其 state 与原对象共享。
③ 在 run() 或外部调度器中,当 status 变为 2 或 3 时,遍历并依次调用所有 continuations —— 这是链式触发的实际执行点。
④ 所有捕获必须用值方式([x = std::move(val)])或 shared_ptr,禁止 this 捕获或引用局部变量,否则链执行中途访问已析构内存。
这一步最关键:Task 对象本身不拥有线程,它只定义“做什么”和“谁来接着做”,调度权交给外部线程池或事件循环。

















