std::packaged_task包装可调用对象时必须指定完整签名,包括返回值、参数类型及const/volatile限定符;其future只能通过get_future()获取且仅一次;执行task()同步触发future就绪,支持异常传播;仅可移动不可拷贝,常用于任务队列或线程池。

std::packaged_task 包装可调用对象时必须指定完整签名
std::packaged_task 不是泛型容器,它本身是模板类,模板参数就是目标函数的完整类型签名,比如 std::packaged_task<int std::string></int>。漏掉返回值或任意一个参数类型,编译直接失败。
常见错误是误以为能像 std::function 那样靠类型擦除“宽松”构造:
auto f = []{ return 42; };
// ❌ 错误:没指定签名,无法推导
// std::packaged_task task(f);
// ✅ 正确:显式写出返回值和参数(无参也得写空括号)
std::packaged_task<int()> task(std::move(f));
- 签名必须与被包装对象的调用方式严格匹配,包括 const/volatile 限定符和引用类型
- lambda 有捕获时,不能是临时对象绑定到 const 引用参数;移动捕获的 lambda 要用
std::move传入 - 函数指针、成员函数指针、bind 表达式都支持,但成员函数需绑定对象或使用
std::mem_fn
关联 future 的唯一方式是调用 task.get_future()
std::packaged_task 对象内部持有一个私有 std::promise,get_future() 是获取对应 std::future 的**唯一合法出口**。不能手动 new promise、不能从别处“塞” future 进来。
一旦调用过 get_future(),就不能再调用第二次 —— 否则抛 std::future_error(错误码为 std::future_errc::future_already_retrieved):
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::packaged_task<int()> task([]{ return 123; });
auto fut1 = task.get_future(); // ✅
// auto fut2 = task.get_future(); // ❌ 抛异常
- future 和 task 是“一对一生效”关系:task 执行后,fut 状态变为 ready,值/异常自动就位
- task 可以 move 到其他线程执行,fut 可在原线程等待,这是跨线程传递结果的基础
- 如果 task 构造后从未调用
get_future()就被销毁,且未执行,对应的 promise 会放弃(future 等待将抛std::future_error)
执行 task 会触发 future 就绪,但异常也会传播
调用 task() 或 task.operator()() 是触发异步任务执行并使 future 就绪的唯一方式。这个调用是同步的 —— 它不启动新线程,只是立即执行封装的可调用对象。
执行中若抛异常,future 会保存该异常,后续 fut.get() 会重新抛出:
std::packaged_task<int()> task([]{
throw std::runtime_error("boom");
});
auto fut = task.get_future();
task(); // ✅ 立即执行,异常被捕获并存入 future
try {
fut.get(); // ❌ 这里才真正抛出 runtime_error
} catch (const std::exception& e) {
// 处理 boom
}
- task 执行完后,其内部 promise 自动 fulfill,future 状态切换为
ready - future 的
wait()、wait_for()、wait_until()都依赖这个就绪信号 - task 被 move 走后原对象处于有效但未定义状态,不能再调用,也不能再取 future
和 std::async / std::thread 搭配使用的典型模式
std::packaged_task 的核心价值在于把“可执行单元 + 返回通道”打包成一个可移动、可存储、可延迟调用的对象,常用于任务队列或线程池调度。
例如投递到 std::thread:
std::packaged_task<int()> task([]{ return 42; });
auto fut = task.get_future();
std::thread t(std::move(task)); // task 已 move,t 执行时自动触发 future 就绪
t.detach(); // 或 join()
// 主线程等结果
int res = fut.get(); // 阻塞直到 t 执行完
- 不能直接把 packaged_task 传给
std::async:async 内部已管理 promise/future,重复包装会导致资源冲突 - 若需线程安全的任务队列,task 对象本身可拷贝?不行 —— 它不可拷贝,只可 move;队列应存
std::packaged_task<...>的右值引用或智能指针包裹 - 多个 future 等待同一个 task 结果?不可能 —— 每个 task 只有一个 future,想共享结果要用
std::shared_future(由future.share()得到)

















