以下代码使用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
,您可以使用mutable
lambda,它可以由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
$