2

I encountered yet another problem with std::thread & this time while applying std::move to swap 2 values. My code says :-

#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;
}

Output :-

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

Now what's wrong in this method ? Why is such the code showing such a behaviour ? How do I correct it ?

Ankit Acharya
  • 2,833
  • 3
  • 18
  • 29

2 Answers2

3

It's because the thread constructor you're calling

copies/moves all arguments (both the function object f and all args...) to thread-accessible storage as if by the function:

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

And std::decay will remove cv-qualifiers, which includes r-value references.

Thus, when std::thread is copy/moving arguments to thread-accessible storage, it's essentially move constructing it's own ints from the ones you provided, and because a move on an int is simply a copy, when you perform swapno on its values, you're doing it on copies.

To correct it, use std::ref plus swap:

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

Live Demo

AndyG
  • 39,700
  • 8
  • 109
  • 143
  • so according to you we can't use `rvalue references` with `threads` ? – Ankit Acharya Nov 24 '15 at 15:37
  • 1
    @AnkitAcharya: Not exactly. You shouldn't use rvalue references when you want to access the data afterwards. In your case, `std::move` on an int is useless, but if you wanted to perhaps give the thread it's own `vector` to work on (which you constructed in the main thread), then `std::move`'ing that `vector` is useful. – AndyG Nov 24 '15 at 15:40
0

In layman's terms, thread's constructor accepts temporaries/rvalues to the arguments passed to the function. Therefore you have to wrap it with a reference_wrapper, which is a value but wraps the underlying reference (what std::ref does).

The code below does the trick with std::swap out of the box. Using std::swap as argument to thread (or std::function) causes and ambiguous overload (which surprised me too).

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;
}
Werner Erasmus
  • 3,988
  • 17
  • 31