The following code uses std::bind_front to bind an object of move-only type as the first argument to a function, then constructs a std::packaged_task from the resulting function object. (Try it on Godbolt.)
#include <future>
#include <functional>
struct MoveOnly {
int v;
MoveOnly(int v) : v(v) {}
MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator=(const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) = default;
MoveOnly& operator=(MoveOnly&&) = default;
};
int foo(MoveOnly m) {
return m.v;
}
int main(int argc, char* argv[]) {
std::packaged_task<int()> task(std::bind_front(foo, MoveOnly(3)));
}
This code does not compile with GCC 12.1.0 using g++ -std=c++20 repro.cpp -o repro.exe
. I get the following error message.
In file included from repro.cpp:1:
/usr/include/c++/12.1.0/future: In instantiation of ‘void std::__future_base::_Task_state<_Fn, _Alloc, _Res(_Args ...)>::_M_run(_Args&& ...) [with _Fn = std::_Bind_front<int (*)(MoveOnly), MoveOnly>; _Alloc = std::allocator<int>; _Res = int; _Args = {}]’:
/usr/include/c++/12.1.0/future:1466:7: required from here
/usr/include/c++/12.1.0/future:1469:41: error: no matching function for call to ‘__invoke_r<int>(std::_Bind_front<int (*)(MoveOnly), MoveOnly>&)’
1469 | return std::__invoke_r<_Res>(_M_impl._M_fn,
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
1470 | std::forward<_Args>(__args)...);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/12.1.0/tuple:41,
from /usr/include/c++/12.1.0/mutex:38,
from /usr/include/c++/12.1.0/future:38:
/usr/include/c++/12.1.0/bits/invoke.h:104:5: note: candidate: ‘template<class _Res, class _Callable, class ... _Args> constexpr std::enable_if_t<is_invocable_r_v<_Res, _Callable, _Args ...>, _Res> std::__invoke_r(_Callable&&, _Args&& ...)’
104 | __invoke_r(_Callable&& __fn, _Args&&... __args)
| ^~~~~~~~~~
/usr/include/c++/12.1.0/bits/invoke.h:104:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/12.1.0/bits/stl_pair.h:60,
from /usr/include/c++/12.1.0/tuple:38:
/usr/include/c++/12.1.0/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = int]’:
/usr/include/c++/12.1.0/bits/invoke.h:104:5: required by substitution of ‘template<class _Res, class _Callable, class ... _Args> constexpr std::enable_if_t<is_invocable_r_v<_Res, _Callable, _Args ...>, _Res> std::__invoke_r(_Callable&&, _Args&& ...) [with _Res = int; _Callable = std::_Bind_front<int (*)(MoveOnly), MoveOnly>&; _Args = {}]’
/usr/include/c++/12.1.0/future:1469:34: required from ‘void std::__future_base::_Task_state<_Fn, _Alloc, _Res(_Args ...)>::_M_run(_Args&& ...) [with _Fn = std::_Bind_front<int (*)(MoveOnly), MoveOnly>; _Alloc = std::allocator<int>; _Res = int; _Args = {}]’
/usr/include/c++/12.1.0/future:1466:7: required from here
/usr/include/c++/12.1.0/type_traits:2614:11: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
2614 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
| ^~~~~~~~~~~
/usr/include/c++/12.1.0/future: In instantiation of ‘void std::__future_base::_Task_state<_Fn, _Alloc, _Res(_Args ...)>::_M_run_delayed(_Args&& ..., std::weak_ptr<std::__future_base::_State_baseV2>) [with _Fn = std::_Bind_front<int (*)(MoveOnly), MoveOnly>; _Alloc = std::allocator<int>; _Res = int; _Args = {}]’:
/usr/include/c++/12.1.0/future:1476:7: required from here
/usr/include/c++/12.1.0/future:1479:41: error: no matching function for call to ‘__invoke_r<int>(std::_Bind_front<int (*)(MoveOnly), MoveOnly>&)’
1479 | return std::__invoke_r<_Res>(_M_impl._M_fn,
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
1480 | std::forward<_Args>(__args)...);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/12.1.0/bits/invoke.h:104:5: note: candidate: ‘template<class _Res, class _Callable, class ... _Args> constexpr std::enable_if_t<is_invocable_r_v<_Res, _Callable, _Args ...>, _Res> std::__invoke_r(_Callable&&, _Args&& ...)’
104 | __invoke_r(_Callable&& __fn, _Args&&... __args)
| ^~~~~~~~~~
/usr/include/c++/12.1.0/bits/invoke.h:104:5: note: template argument deduction/substitution failed:
I only get this error using std::bind_front. std::packaged_task accepts other move-only callables without any problem, and I am using this in my actual code (which queues tasks to be executed in a thread pool). For example, if you add an operator() to MoveOnly, you can construct a task from a MoveOnly object.
What makes std::bind_front different and how can I adapt the function objects from std::bind_front to work in std::packaged_tasks?
bind_front
itself stores a copy of MoveOnly
.
Since MoveOnly
can't be copied, we need to use std::move
to move it to foo
, which makes only rvalue ref qualifier operator() &&
valid in bind_front
's returning function wrapper:
auto f = std::bind_front(foo, MoveOnly(3));
f(); // not ok
std::move(f)(); // ok
And because packaged_task
always invokes the underlying function through an lvalue, so compile fails.
Instead of using bind_front
, you can use a mutable
lambda, which can be invoked by lvalue
auto bind_front = [m = MoveOnly(3)] mutable { return foo(std::move(m)); };
std::packaged_task<int()> task(std::move(bind_front));