You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PPL create_task要求拷贝构造函数,如何适配仅可移动对象?

问题:将仅可移动对象传入PPL create_task的解决办法

问题描述

以下代码尝试使用PPL的create_task创建任务,lambda捕获了仅可移动对象,但编译失败:

#include <ppltasks.h>

#include <future>
#include <iostream>
#include <thread>

void third_party_lib_call_that_blocks(std::string message) {
  std::cout << message << "\n";
  std::this_thread::sleep_for(std::chrono::seconds(1));
}

class nocopy {
 public:
  explicit nocopy() {}

  nocopy(const nocopy& _Other) = delete;
  nocopy(nocopy&& _Other) = default;
  nocopy& operator=(const nocopy&) = delete;
  nocopy& operator=(nocopy&&) = default;
};

int main() {
  std::string message = "hello";
  nocopy n;

  auto t = concurrency::create_task([message = std::move(message), n = std::move(n)]() {
    std::cout << "start\n";
    third_party_lib_call_that_blocks(message);
    std::cout << "end\n";
  });

  t.wait();

  return 0;
}

编译错误

C:/data/msvc/14.34.31931-Pre/include\ppltasks.h(4544): error C2338: static_assert failed: 'incorrect argument for create_task; must be either a callable object or a task_completion_event'
<source>(30): note: see reference to function template instantiation 'Concurrency::task<Concurrency::details::_BadArgType> Concurrency::create_task<main::<lambda_1>>(_Ty,Concurrency::task_options)' being compiled
        with
        [
            _Ty=main::<lambda_1>
        ]
Execution build compiler returned: 2

问题根源:PPL的create_task要求传入的可调用对象必须具备拷贝构造函数,但捕获仅可移动对象后的lambda本身会变成仅可移动类型,无法满足要求。


解决办法

方法1:用std::shared_ptr包装仅可移动对象

将仅可移动对象托管到std::shared_ptr中,lambda捕获可拷贝的shared_ptr,间接持有原对象:

int main() {
  std::string message = "hello";
  auto n = std::make_shared<nocopy>();

  auto t = concurrency::create_task([message = std::move(message), n]() {
    std::cout << "start\n";
    third_party_lib_call_that_blocks(message);
    std::cout << "end\n";
  });

  t.wait();

  return 0;
}

说明:std::shared_ptr本身是可拷贝的,lambda捕获后可以被create_task正常拷贝,而实际的nocopy对象存储在堆内存中,通过shared_ptr访问即可。

方法2:通过std::packaged_task中转

利用std::packaged_task可以接收仅可移动可调用对象的特性,将lambda包装后,把对应的std::future传入create_task:

int main() {
  std::string message = "hello";
  nocopy n;

  auto task_func = [message = std::move(message), n = std::move(n)]() {
    std::cout << "start\n";
    third_party_lib_call_that_blocks(message);
    std::cout << "end\n";
  };

  std::packaged_task<void()> pt(std::move(task_func));
  auto future = pt.get_future();

  // 启动线程执行packaged_task
  std::thread([pt = std::move(pt)]() mutable { pt(); }).detach();

  // 基于future创建PPL任务
  auto t = concurrency::create_task(std::move(future));
  t.wait();

  return 0;
}

说明:packaged_task可以接管仅可移动的lambda,create_task可以接受std::future作为参数。需要注意必须单独启动线程执行packaged_task,否则future会一直处于等待状态。


针对asio::async_compose中self参数的处理

对于asio的async_compose传递的auto&& self(仅可移动的completion handler),同样可以用std::shared_ptr包装:

asio::async_compose<Token, void(std::error_code)>(
    [message = std::move(message)](auto&& self) mutable {
      // 将self包装到shared_ptr中
      auto self_ptr = std::make_shared<decltype(self)>(std::move(self));
      concurrency::create_task(
          [message = std::move(message), self_ptr]() mutable {
            auto ec = third_party_lib_call_that_blocks(std::move(message));
            (*self_ptr).complete(ec);
          });
    },
    token, executor_);

说明:decltype(self)推导的是仅可移动的handler类型,用shared_ptr持有后,lambda可以安全拷贝shared_ptr,在任务执行时解引用调用complete即可完成异步回调。


内容的提问来源于stack exchange,提问作者MHebes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:30:49