5

您不能将对象移动到另一个对象的原因是什么std::thread?在某些情况下它可能有用。例如:

您创建一个接受传入套接字连接的循环。将传入连接移动到将处理连接的另一个线程会很好。在接受循环中不再需要连接。那么为什么要创建一个指针呢?

一个小测试用例:

#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 &&'**
}
4

2 回答 2

16

std::thread构造函数必须以与大多数转发函数不同的方式对待您提供的参数。

其原因是由于线程实际何时开始的问题。如果实际创建函数参数的函数调用部分在thread对象创建后很久才运行(这是完全合法的行为),那么需要从中移动的对象可能早就被销毁了。

只需考虑代码的更改版本:

std::thread some_func()
{
    Pointertest pointertest;

    thread test(foo,std::move(pointertest));
    return test;
}

这是完全有效的(线程将被移出函数)。但是,有一个大问题。foo可能还没有被调用。并且由于foo通过引用获取其参数,它现在具有对已销毁的堆栈变量的引用。

那很糟。但即使foo按值取参数,它也不会改变任何东西。因为直到线程启动后的某个不确定时间才会实际移动到该参数中。移入参数的尝试仍将使用对已销毁的堆栈变量的右值引用。这又是坏事。

因此,std::thread构造函数做了一些不同的事情。它将您提供的参数复制/移动到内部存储中(这是在当前线程上完成的)。然后它使用这些值作为实际函数调用的参数(这是在新线程上完成的)。

根据标准,线程构造函数应该将这些内部变量作为临时变量传递给您的函数。该标准特别指出INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)DECAY_COPY事情发生在原始线程上,而INVOKE部分发生在新线程上。

因此,您的实现似乎thread无法正确转发不可复制的参数。您应该能够传递不可复制的类型;参数只需要是MoveConstructible.

所以这似乎是您实现中的一个错误。

于 2013-05-13T18:12:06.880 回答
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();
//  ^^^^^^^^^^^^
}
于 2013-05-13T17:49:06.403 回答