使用仅限移动的绑定参数从std::bind_front构造std::packaged_task不会编译



以下代码使用std::bind_front将仅移动类型的对象作为第一个参数绑定到函数,然后从生成的函数对象构造std::packaged_task。(在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)));
}

此代码不使用g++ -std=c++20 repro.cpp -o repro.exe与GCC 12.1.0一起编译。我收到以下错误消息。

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:

我只使用std::bind_front得到这个错误。std::packaged_task可以毫无问题地接受其他仅限移动的可调用程序,我在实际的代码中使用了这一点(将要在线程池中执行的任务排队(。例如,如果将运算符((添加到MoveOnly,则可以从MoveOnly对象构造任务。

std::bind_front的区别是什么?我如何调整std::bind_front中的函数对象以在std::packaged_tasks中工作?

bind_front本身存储MoveOnly的副本。

由于MoveOnly无法复制,我们需要使用std::move将其移动到foo,这使得只有右值引用限定符operator() &&bind_front的返回函数包装器中有效

auto f = std::bind_front(foo, MoveOnly(3));
f(); // not ok
std::move(f)(); // ok

由于packaged_task总是通过左值调用底层函数,因此编译失败。

不使用bind_front,您可以使用mutablelambda,它可以由lvalue调用

auto bind_front = [m = MoveOnly(3)] mutable { return foo(std::move(m)); };
std::packaged_task<int()> task(std::move(bind_front));
@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
$

最新更新