我一直在学习完美转发和使用&&在函数模板中(请参阅我的this previous question),并想知道我在Args&&
下使用StartDetachedThread()
是否合理:
#include <thread>
class CObject {};
void MyThreadFunc(CObject&)
{
}
// ** Will not compile with this function declaration! **
void MyThreadFunc(CObject&&)
{
}
template<typename FunctionType, typename ...Args>
void StartDetachedThread(FunctionType func, Args&&... args)
{
thread([&]()
{
func(forward<Args>(args)...);
}).detach();
}
int main()
{
CObject object;
StartDetachedThread(MyThreadFunc, object);
CObject object2;
StartDetachedThread(MyThreadFunc, std::move(object2));
return 0;
}
这段代码只是创建一个分离的线程,运行提供的函数,并将提供的参数传递给它。
然而,VS 2017抱怨:
'StartDetachedThread': no matching overloaded function found
'void StartDetachedThread(FunctionType,Args &&...)': could not deduce template argument for 'FunctionType'
1)我知道传递给thread
构造函数的参数首先被复制,然后通过引用传递给新线程,所以当我传递一个右值引用时,我尝试调用MyThreadFunc(CObject&&)
去上班?
2)拥有StartDetachedThread(FunctionType&& func, Args&&... args)
是否有任何价值 - &&
是否需要FunctionType
?
3)在启动这样的线程时使用Args&&
是否有任何价值,或者我应该始终使用Args
吗?
答案 0 :(得分:1)
问题是编译器无法推导出MyThreadFunc
的哪个重载。至少有两种方法可以解决它:
重命名其中一个功能,以便更清楚您想要的功能。
使用显式模板参数:
StartDetachedThread<void (CObject&)>(MyThreadFunc, object);
StartDetachedThread<void (CObject&&)>(MyThreadFunc, std::move(object2));
答案 1 :(得分:1)
您的代码中的问题与std::thread
无关,这是因为MyThreadFunc
在此上下文中含糊不清:
// Which MyThreadFunc should be used?
StartDetachedThread(MyThreadFunc, object);
关于你的问题:
1)我知道传递给线程构造函数的参数首先被复制,然后通过引用传递给新线程,[...]
在您的示例中,唯一的副本是lambda的副本。这里不会复制参数,如果你想复制参数,你应该使用这样的东西:
std::thread(std::move(func), std::forward<Args>(args)...).detach();
...将参数转发给std::thread
构造函数。
这更安全。 - 想想如果函数StartDetachedThread
在线程仍在运行时结束会发生什么?
如果您使用此功能,则需要使用object1
明确告诉编译器您要为std::ref
调用参考版本:
CObject object;
StartDetachedThread<void (CObject&)>(MyThreadFunc, std::ref(object)); // std::ref
CObject object2;
StartDetachedThread<void (CObject&&)>(MyThreadFunc, std::move(object2));
2)拥有
StartDetachedThread(FunctionType&& func, Args&&... args)
是否有任何价值 -&&
是否需要FunctionType
?3)在启动这样的线程时使用
Args&&
是否有任何价值,或者我应该始终使用Args
吗?
使用转发引用可以调用StartDetachedThread
而无需移动所有内容。如果您使用上述方式构建std::thread
,则无论如何都会为func
和args
制作副本。