I'm actually working to delete most of my copy constructor/assignation, and I faced an issue when comming to lambda use:
ThreadPool:
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();
}
My actual issue:
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
Datamember (which is intentionally non-copyable).However,
std::functionby design requires all callable that it stores to be copyable so that thestd::functionobject itself can also be copied with value semantics (i.e. the stored callable is actually copied, twostd::functioninstances 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_functionwhich can't be copied and so doesn't require the stored callable to be copyable either. It also improves on some other issues withstd::function's design. In C++26 there will alsostd::copyable_functionwhich is again copyable by virtue of requiring stored callables to be copyable while also makingstd::move_only_function's other design improvements overstd::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 toenqueue, it would be sufficient to replaceTaskwith an unconstrained template parameter to accept the lambda directly.