Passing object by reference to std::thread in C++11

C++MultithreadingC++11Pass by-ReferenceStdthread

C++ 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 fromstd::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 ‘typeinclass 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 ‘typeinclass 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;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionaustinmartonView Question on Stackoverflow
Solution 1 - C++ShadowRangerView Answer on Stackoverflow
Solution 2 - C++ネロクView Answer on Stackoverflow
Solution 3 - C++Jarod42View Answer on Stackoverflow
Solution 4 - C++Oleg KokorinView Answer on Stackoverflow