我正在尝试为std::thread
编写包装器:
#include <thread>
#include <iostream>
struct A {};
template <typename F, typename... Args>
void lifted_lambda_1(void *m, F &&entrypoint, Args&&... args) {
std::cout << "I will do something with the void * " << m << std::endl;
entrypoint(std::forward<Args>(args)...);
}
template <typename F, typename... Args>
void make_thread(void *p, F &&f, Args && ... args) {
std::thread(lifted_lambda_1<typename std::decay<F>::type, Args...>, p, std::forward<F>(f), std::forward<Args>(args)...).detach();
}
int main() {
A a;
make_thread(nullptr, [](A x){}, a);
}
但是当我编译它时,我得到一个错误:
In file included from /usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/thread:39:0,
from bubu.cpp:1:
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional: In instantiation of ‘struct std::_Bind_simple<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’:
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/thread:137:47: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void*, main()::__lambda0&&, A&); _Args = {void*&, main()::__lambda0, A&}]’
bubu.cpp:15:132: required from ‘void make_thread(void*, F&&, Args&& ...) [with F = main()::__lambda0; Args = {A&}]’
bubu.cpp:20:38: required from here
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’
_M_invoke(_Index_tuple<_Indices...>)
此错误的原因是什么?我如何解决它?
最佳答案
std::thread
将始终衰减其自变量(出于上述注释之一中的链接给出的原因)。您可以使用reference_wrapper
保护引用,以便可以通过左值引用传递参数。
为了同时使用左值和右值参数,您需要一个包装函数,该函数将左值包装在reference_wrapper
中,但允许右值被复制(或移动)并作为右值转发。这不会是“完美的”转发,因为右值将被复制,而不是作为右值引用转发,因此目标函数将被新对象调用。
因此,您可以使用如下方式有条件地包装左值,而仅转发右值:
template<typename T>
std::reference_wrapper<std::remove_reference_t<T>>
wrap(T& t) { return std::ref(t); }
template<typename T>
T&&
wrap(typename std::remove_reference<T>::type&& t)
{ return std::move(t); }
(
remove_reference
用于第二次重载,因此T
在非推论上下文中,因此该参数不是转发引用)。然后将其用作线程构造函数的参数:
std::thread(lifted_lambda_1<typename std::decay<F>::type, Args...>, p,
std::forward<F>(f), wrap<Args>(args)...).detach();
/*^^^^^^^^^^^^*/
但是,这样做会带来
std::thread
试图通过复制其参数来避免的所有问题!您必须确保在线程完成运行之前,传递给make_thread
的所有左值都不会超出范围。由于要分离线程,因此通常很难做到这一点。使用此功能时必须非常小心。可能您可以编写自己的类模板,该类模板的行为类似于保护右值引用的
reference_wrapper
,以避免创建新的对象,但是您还必须注意线程函数的右值参数在线程完成之前不会超出范围运行(如果它们是右值,则很有可能它们是临时对象,不会超过创建新线程的调用!)这是龙。
关于c++ - 实现一个完美转发到std::thread的函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28820271/