Home > Blockchain >  Constructing std::packaged_task from std::bind_front with a move-only bound argument does not compil
Constructing std::packaged_task from std::bind_front with a move-only bound argument does not compil

Time:07-30

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?

CodePudding user response:

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));

CodePudding user response:

@Jeffrey Bosboom
View my comments("IDA30072022100841").
#include <iostream>
using namespace std;
#include <future>
#include <functional>
struct MoveOnly
{
 public:
        // I have not modified this code to use private/public/protected kind of actions to given variables or functions.
        int v;
        MoveOnly(int v):v(v)
        {
        }
        MoveOnly(const MoveOnly&) = delete;
        MoveOnly& operator=(const MoveOnly&) = delete;
        MoveOnly(MoveOnly&&) = default;
        MoveOnly& operator=(MoveOnly&&) = default;
};
/*
 * IDA30072022100841
 * ERROR:
 *      error: no matching function for call to ‘__invoke_r<int>std::_Bind_front<int (*)(MoveOnly), MoveOnly>&)
 * DESCRIPTION:
 *      First parameter to bind_front is a function pointer to a function accepting reference.
 *      However previous code was having that funciton without using reference.
 * COMPILATION:
 *      g  .exe --std=c  20 73172857.cpp -o ./a.out
REPLACE:
 int foo(MoveOnly m)
WITH:
 int foo(MoveOnly& m)
*/
int foo(MoveOnly& m)
{
 return m.v;
}
int main(int argc, char* argv[])
{
 std::packaged_task< int() > task( std::bind_front(foo, MoveOnly(3)) );
 return 0;
}
$ g   --std=c  20  73172857.cpp -o ./a.out
$ echo Compilation success full $?.
Compilation success full 0.
$ ./a.out
$
  • Related