Passing object by reference to std::thread in C++11
C++MultithreadingC++11Pass by-ReferenceStdthreadC++ Problem Overview
Why can't you pass an object by reference when creating a std::thread
?
For example the following snippit gives a compile error:
#include <iostream>
#include <thread>
using namespace std;
static void SimpleThread(int& a) // compile error
//static void SimpleThread(int a) // OK
{
cout << __PRETTY_FUNCTION__ << ":" << a << endl;
}
int main()
{
int a = 6;
auto thread1 = std::thread(SimpleThread, a);
thread1.join();
return 0;
}
Error:
In file included from /usr/include/c++/4.8/thread:39:0,
from ./std_thread_refs.cpp:5:
/usr/include/c++/4.8/functional: In instantiation of ‘struct std::_Bind_simple<void (*(int))(int&)>’:
/usr/include/c++/4.8/thread:137:47: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(int&); _Args = {int&}]’
./std_thread_refs.cpp:19:47: required from here
/usr/include/c++/4.8/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/include/c++/4.8/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’
_M_invoke(_Index_tuple<_Indices...>)
^
I've changed to passing a pointer, but is there a better work around?
C++ Solutions
Solution 1 - C++
Explicitly initialize the thread with a reference_wrapper
by using std::ref
:
auto thread1 = std::thread(SimpleThread, std::ref(a));
(or std::cref
instead of std::ref
, as appropriate). Per notes from cppreference on std:thread
:
>The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g. with std::ref
or std::cref
).
Solution 2 - C++
Based on this comment, this answer elaborates on the reason why the arguments are not passed by reference to the thread function by default.
Consider the following function SimpleThread()
:
void SimpleThread(int& i) {
std::this_thread::sleep_for(std::chrono::seconds{1});
i = 0;
}
Now, imagine what would happen if the following code compiled (it does not compile):
int main()
{
{
int a;
std::thread th(SimpleThread, a);
th.detach();
}
// "a" is out of scope
// at this point the thread may be still running
// ...
}
The argument a
would be passed by reference to SimpleThread()
. The thread may still be sleeping in the function SimpleThread()
after the variable a
has already gone out of scope and its lifetime has ended. If so, i
in SimpleThread()
would actually be a dangling reference, and the assignment i = 0
would result in undefined behaviour.
By wrapping reference arguments with the class template std::reference_wrapper
(using the function templates std::ref
and std::cref
) you explicitly express your intentions.
Solution 3 - C++
std::thread
copy(/move) its arguments, you might even see the note:
> The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g., with std::ref
or std::cref
).
So, you might use std::reference_wrapper
through std::ref
/std::cref
:
auto thread1 = std::thread(SimpleThread, std::ref(a));
or use lambda:
auto thread1 = std::thread([&a]() { SimpleThread(a); });
Solution 4 - C++
don't pass by reference if your object is stack based allocated, pass by pointer to the new object created in the call of thread API. such object will live as long as thread but should be explicitly deleted before the thread termination.
example:
void main(){
...
std::string nodeName = "name_assigned_to_thread";
std::thread nodeThHandle = std::thread(threadNODE, new std::string(nodeName));
...
}
void threadNODE(std::string *nodeName){
/* use nodeName everywhere but don't forget to delete it before the end */
delete nodeName;
}