您无法将对象移动到另一个std::thread
的原因是什么?有些情况下它可能有用。例如:
您创建一个接受传入套接字连接的循环。将传入连接移动到另一个将处理连接的线程会很好。在accept循环中不再需要连接。那你为什么要创建一个指针呢?
一个小测试用例:
#include <iostream>
#include <thread>
using namespace std;
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest &pointertest) {cout << "Copy";}
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
void foo(Pointertest &&pointertest)
{
}
int main()
{
Pointertest pointertest;
foo(std::move(pointertest)); //Works
thread test(foo,std::move(pointertest)); //**cannot convert parameter 1 from 'Pointertest' to 'Pointertest &&'**
}
答案 0 :(得分:15)
std::thread
构造函数必须将您提供的参数与大多数转发函数区别对待。
原因在于线程实际启动的时间问题。如果实际创建函数参数的函数调用部分在创建thread
对象之后很久就会运行(这是完全合法的行为),那么需要移动的对象可能早已被销毁。
只需考虑代码的更改版本:
std::thread some_func()
{
Pointertest pointertest;
thread test(foo,std::move(pointertest));
return test;
}
这完全有效(线程将被移出函数)。但是,这是一个很大的问题。可能尚未调用foo
。由于foo
通过引用获取其参数,因此它现在引用了已被销毁的堆栈变量。
那很糟糕。但即使foo
按值获取其参数,它也不会改变任何东西。因为直到线程启动后的某个不确定时间才会发生到该参数的实际移动。尝试移入参数仍将使用对已销毁的堆栈变量的rvalue引用。这又是坏事。
因此,std::thread
构造函数做了不同的事情。它将您提供的参数复制/移动到内部存储中(这在当前线程上完成)。然后它使用这些值作为实际函数调用的参数(这在新线程上完成)。
根据标准,线程构造函数应该将这些内部变量作为 temporaries 传递给您的函数。该标准明确指出INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)
,其中DECAY_COPY
内容发生在原始线程上,而INVOKE
部分发生在新线程上。
因此,您的thread
实现似乎无法正确转发不可复制的参数。您应该能够传递不可复制的类型;参数只需要MoveConstructible
。
因此,这似乎是您实施中的错误。
答案 1 :(得分:5)
有可能。修复复制构造函数的签名使它对我有用:
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest const& pointertest) {cout << "Copy";}
// ^^^^^^
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
另外,在thread
对象超出范围之前,不要忘记加入你的线程(或从中断开):
int main()
{
Pointertest pointertest;
thread test(foo, std::move(pointertest));
test.join();
// ^^^^^^^^^^^^
}