在std::thread中使用std::move

3

我在使用std::thread时又遇到了问题,这一次是在应用std::move交换两个值时。我的代码如下:

#include <iostream>
#include <thread>
using namespace std;
void swapno (int &&a, int &&b)
{
    int temp=move(a);
    a=move(b);
    b=move(temp);
}
int main()
{
    int x=5, y=7;
    cout << "x = " << x << "\ty = " << y << "\n";
//  swapno (move(x), move(y));  // this works fine
    thread t (swapno, move(x), move(y));
    t.join();
    cout << "x = " << x << "\ty = " << y << "\n";
    return 0;
}

输出:

x = 5   y = 7
x = 5   y = 7

现在这个方法有什么问题?为什么这段代码会显示这样的行为?我该如何纠正它?

线程使用 xy 的副本而不是引用它们。你可以传递 xy 的指针代替。 - undefined
原因与您之前的问题相同。此外,请使用std::swap - undefined
1
你是怎么从“你必须将它包装在std::ref中”转而使用&&的呢? - undefined
尝试同时进行两个操作 @默认 :P - undefined
2
交换临时变量在@WernerErasmus的代码中有效,请尝试在不使用线程的情况下使用swapno函数。 - undefined
1
在使用std::move(x)之后,变量x不能再作为右值使用,因为它的值已经被移动到另一个变量中。 - undefined
2个回答

3

这是因为你调用的thread构造函数会将所有参数(包括函数对象f和所有参数)复制/移动到线程可访问的存储中,就像通过以下函数一样:

...

template <class T>
typename decay<T>::type decay_copy(T&& v) {
    return std::forward<T>(v);
}

std::decay可以去除cv-qualifier,其中包括右值引用。

因此,当std::thread将参数复制/移动到线程可访问的存储中时,它基本上是从提供的int移动构造了自己的int,并且因为对int进行move只是一次复制,所以当您在其值上执行swapno时,您是在进行副本操作。

要更正它,请使用std::ref加上swap

std::thread t ([](int& a, int& b){std::swap(a, b);}, std::ref(x), std::ref(y));
t.join();

Live Demo


所以根据你的说法,我们不能在线程中使用rvalue引用吗? - undefined
1
@AnkitAcharya:并不完全准确。如果您之后要访问数据,就不应该使用右值引用。在您的情况下,对整数使用std::move是无效的,但如果您想为线程提供自己的vector<T>来处理(在主线程中构造),那么std::move这个vector是有用的。 - undefined

0
简单来说,线程的构造函数接受传递给函数的临时值/右值作为参数。因此,您必须使用reference_wrapper将其包装起来,它是一个值,但包装了底层引用(就像std::ref所做的那样)。
下面的代码可以直接使用std::swap来解决问题。使用std::swap作为thread(或std::function)的参数会导致重载模糊(这也让我感到惊讶)。
int main(int argc, char* argv[])
{
  int x = 5, y = 7;
  std::cout << "x(" << x << ")" << " y(" << y <<")" << std::endl;
  void (*pf)(int& x, int& y) = std::swap<int>;
  std::thread t1(pf, std::ref<int>(x), std::ref<int>(y));
  t1.join();
  std::cout << "x(" << x << ")" << " y(" << y <<")" << std::endl;
  return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接