为什么不可能将变量移动到另一个std :: thread

Ord*_*rdo 5 c++ windows move c++11 visual-studio-2012

你不能将一个物体移动到另一个物体的原因是什么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 &&'**
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 16

std::thread构造函数把你给它多少有些不同于大多数转发功能的参数.

造成这种情况的原因是由于线程实际启动的时间问题.如果实际创建函数参数的函数调用部分在创建thread对象后很久就会运行(这完全是合法行为),那么需要移动的对象可能早就被销毁了.

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

std::thread some_func()
{
    Pointertest pointertest;

    thread test(foo,std::move(pointertest));
    return test;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

所以这似乎是你实现中的一个错误.


And*_*owl 5

有可能的.修复复制构造函数的签名使它适用于我:

class Pointertest
{
public:
    Pointertest() {cout << "Constructor";}
    Pointertest(Pointertest const& pointertest) {cout << "Copy";}
//                          ^^^^^^
    Pointertest(Pointertest &&pointertest) {cout << "Move";}
    ~Pointertest() {cout << "Destruct";}
};
Run Code Online (Sandbox Code Playgroud)

此外,在thread对象超出范围之前,不要忘记加入您的线程(或从中分离):

int main()
{
    Pointertest pointertest;
    thread test(foo, std::move(pointertest));

    test.join();
//  ^^^^^^^^^^^^
}
Run Code Online (Sandbox Code Playgroud)

  • @Ordo:"运动"并不意味着析构函数永远不会被调用.当你从一个对象移动时,你应该让它处于一个仍然*功能*的状态(这样它的析构函数就可以工作).但是你从中获取了所有重要的部分.每个构造函数最终仍然会被析构函数跟踪; 运动并不能阻止这一点. (3认同)