Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error on moving a packaged_task object to lambda capture

I am implementing a threadpool that has a push_back method on callable object. However I am getting error on moving a packaged task into a function object using lambda trick.

class Threadpool {

public:
  // ...
  ::std::deque <::std::function<void()>> _work_queue;
  ::std::mutex _work_queue_mutex;
  ::std::condition_variable _worker_signal;

  template <typename CallableT>
  ::std::future<::std::result_of_t<CallableT()>> push_back(CallableT&&);
}

template<typename CallableT>
::std::future<::std::result_of_t<CallableT()>> Threadpool::push_back(CallableT&& callable) {

  ::std::packaged_task<::std::result_of_t<CallableT()>()> task (::std::move(callable));
  auto fu = task.get_future();

  {
    ::std::unique_lock<::std::mutex> locker(_work_queue_mutex);
    // COMPILE ERROR
    _work_queue.emplace_back([task=::std::move(task)] () { task(); })
  }

  _worker_signal.notify_one();

  return fu; 
}

Threadpool pool;
pool.emplace_back( []() { ::std::cout << "hello\n"; } );

The compiler complains about the emplace_back by error: no match for call to '(const std::packaged_task<void()>) ()' _work_queue.emplace_back([task=::std::move(task)]() { task(); }); I don't understand what's going wrong since as far as I know packaged_task is only movable and I am capturing the task by move.

like image 775
Jes Avatar asked Jun 01 '26 13:06

Jes


1 Answers

There are two issues with your example.

Indeed, std::packaged_task is only movable, so [task=std::move(task)] is correct. But on top of that std::packaged_task::operator() requires not-const object: https://en.cppreference.com/w/cpp/thread/packaged_task/operator()

So the lambda must be defined as mutable to allow the usage of task():

[task=std::move(task)] () mutable { task(); };

But even so the lambda object is only movable and not copyable, while std::function requires a copyable object: https://en.cppreference.com/w/cpp/utility/functional/function

So one of the solutions, is to wrap the packaged_task in a copyable smart pointer as follows:

#include <mutex>
#include <deque>
#include <functional>
#include <condition_variable>
#include <future>
#include <iostream>
#include <type_traits>

class Threadpool 
{
public:
  // ...
  std::deque <std::function<void()>> _work_queue;
  std::mutex _work_queue_mutex;
  std::condition_variable _worker_signal;

  template <typename CallableT>
  std::future<std::result_of_t<CallableT()>> push_back(CallableT&&);
};

template<typename CallableT>
std::future<std::result_of_t<CallableT()>> Threadpool::push_back(CallableT&& callable) 
{
  auto task = std::make_shared<std::packaged_task<std::result_of_t<CallableT()>()>>( std::move(callable) );
  auto fu = task->get_future();

  {
    std::unique_lock<std::mutex> locker(_work_queue_mutex);
    _work_queue.emplace_back([task]() { (*task)(); });
  }

  _worker_signal.notify_one();

  return fu; 
};

int main()
{
  Threadpool pool;
  pool.push_back( []() { std::cout << "hello\n"; } );
}

Demo: https://gcc.godbolt.org/z/aEfvo7Mhz

like image 180
Fedor Avatar answered Jun 03 '26 03:06

Fedor



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!