39

This is the example:

#include<iostream>
#include<thread>
using namespace std;

void f1(double& ret) {
   ret=5.;
}

void f2(double* ret) {
   *ret=5.;
}

int main() {
   double ret=0.;
   thread t1(f1, ret);
   t1.join();
   cout << "ret=" << ret << endl;
   thread t2(f2, &ret);
   t2.join();
   cout << "ret=" << ret << endl;   
}

And the output is:

ret=0
ret=5

Compiled with gcc 4.5.2, with and without -O2 flag.

Is this expected behavior?

Is this program data race free?

Thank you

3 Answers 3

89

The constructor of std::thread deduces argument types and stores copies of them by value. This is needed to ensure the lifetime of the argument object is at least the same as that of the thread.

C++ template function argument type deduction mechanism deduces type T from an argument of type T&. All arguments to std::thread are copied and then passed to the thread function so that f1() and f2() always use that copy.

If you insist on using a reference, wrap the argument using boost::ref() or std::ref():

thread t1(f1, boost::ref(ret));

Or, if you prefer simplicity, pass a pointer. This is what boost::ref() or std::ref() do for you behind the scene.

Sign up to request clarification or add additional context in comments.

2 Comments

This really helped me, thanks +1. If this message will still be noticed by anyone, may I ask the reason thread uses this mechanism. Has this something to do with c, or does it have a completely different reason?
@patrik Value semantics (copying) are safe compared to storing a reference or a pointer. Reference or a pointer may become dangling when the object they refer to gets destroyed. This is why std::bind and std::thread store arguments by value by default and you have to explicitly ask it to do a less safe thing.
9

If you want to pass parameters by reference to a std::thread you must enclose each of them in std::ref:

thread t1(f1, std::ref(ret));

More info here.

Comments

9

That you are required an explicit std::ref() (or boost::ref()) in these situations is actually a very useful safety feature as passing a reference can be by nature a dangerous thing to do.

With a non-const reference there is quite often a danger that you are passing in a local variable, with a const-reference it might be a temporary, and as you are creating a function to be called in a different thread (and with bind in general, often a function to be called later / in an asynchronous way) you will have the big danger of the object being no longer valid.

binding looks tidy but these bugs are the hardest to find, as where the error is caught (i.e. in calling the function) is not the same place that the error was made (at the time of binding) and it can be very hard to work out exactly which function is being called at the time, and therefore where it was bound.

It is safe in your instance as you join the thread in the scope of the variable you are passing as reference. Therefore when you know that to be the case there is a mechanism for passing a reference.

It is not a feature of the language I would like to see changed, particularly as there is probably a lot of existing code relying on it making a copy that would break if it just took by reference automatically (and would then need an explicit way to force a copy).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.