线程函数中的通用引用

Universal references in a thread function

本文关键字:引用 函数 线程      更新时间:2023-10-16

我一直在学习完美转发和在函数模板中使用&&(请参阅我之前的问题),并想知道我在下面的StartDetachedThread()中使用Args&&是否合理:

#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

你的代码中的问题与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在线程仍在运行时结束会发生什么?

如果使用它,则需要显式告诉编译器要使用std::ref调用object1的参考版本:

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,那么无论如何都会为funcargs制作副本。

问题是编译器无法推断所需的MyThreadFunc重载。 至少有两种方法可以修复它:

  1. 重命名其中一个函数,以便更清楚地了解您想要哪个函数。

  2. 使用显式模板参数:

    StartDetachedThread<void (CObject&)>(MyThreadFunc, object);
    StartDetachedThread<void (CObject&&)>(MyThreadFunc, std::move(object2));