I'm actually working to delete most of my copy constructor/assignation, and I faced an issue when comming to lambda use:
using Task = std::function<void()>;
template<size_t S>
void ThreadPool<S>::enqueue(Task _task)
{
{
std::unique_lock<std::mutex> lock(m_mutex);
m_queue.push(_task);
}
m_cond.notify_one();
}
class MyClass {
public:
MyClass(); // some implementation
~MyClass(); // some implementation
void process(Data &_data)
{
if (_data.data == 1)
m_tp.enqueue([this, data = std::move(_data)] () {
func1(data);
});
else if (_data.data == 2)
m_tp.enqueue([this, data = std::move(_data)] () {
func2(data);
});
else
m_tp.enqueue([this, data = std::move(_data)] () {
func3(data);
});
}
void MyClass::func1(const Data &_data)
{
// use of data
}
// func2 and func3 have the same signature
private:
ThreadPool<4> m_tp{};
};
Data
class only declare move constructor/assignation, but when doing so, I got this error:
> /usr/include/c++/11/bits/std_function.h: In instantiation of ‘std::function<_Res(_ArgTypes ...)>::function(_Functor&&) [with _Functor = MyClass::process(Data&)::<lambda()>; _Constraints = void; _Res = void; _ArgTypes = {}]’:
> /usr/include/c++/11/bits/std_function.h:439:69: error: static assertion failed: std::function target must be copy-constructible
> 439 | static_assert(is_copy_constructible<__decay_t<_Functor>>::value,
> /usr/include/c++/11/bits/std_function.h:439:69: note: ‘std::integral_constant<bool, false>::value’ evaluates to false
> ...
But I can't figure out, how to fix this compilation error and keep the std::move(_data)
in lambda capture. I tried using mutable
but it just give me the same error if I use it on all of the lambda and some similar error if I just use it on one.
The lambda is always going to be non-copyable because its closure type must have a Data
member (which is intentionally non-copyable).
However, std::function
by design requires all callable that it stores to be copyable so that the std::function
object itself can also be copied with value semantics (i.e. the stored callable is actually copied, two std::function
instances don't reference the same callable object).
So you can't store such a lambda in a std::function
.
In C++23 there is std::move_only_function
which can't be copied and so doesn't require the stored callable to be copyable either. It also improves on some other issues with std::function
's design. In C++26 there will also std::copyable_function
which is again copyable by virtue of requiring stored callables to be copyable while also making std::move_only_function
's other design improvements over std::function
.
If you can't use std::move_only_function
, then you'll have to implement analogues move-only type erasure manually in order to store the lambda as a class member. Although, for just the call to enqueue
, it would be sufficient to replace Task
with an unconstrained template parameter to accept the lambda directly.