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

4

112

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?

Exorcism answered 3/12, 2015 at 23:25 Comment(0)
S
147

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).

Succession answered 3/12, 2015 at 23:28 Comment(2)
The capture, sensibly, defaults to capture by value because otherwise things would fail horribly if the parameter went away before the thread could read it. You need to explicitly ask for this behavior so that there's something to indicate that you are assuming responsibility for ensuring that the target of the reference is still valid.Marnie
Excellent! I was hoping there was a way to explicitly pass a reference. C++11 to the rescue again :)Exorcism
B
38

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.

Baccalaureate answered 3/9, 2018 at 19:18 Comment(5)
I think this is some kind of wrong argument on why arguments are not passed by reference because even using std::ref(a) leads to undefined behaviour in this scenario, isn't it ?Redbird
@Redbird Yes, using std::ref(a) still leads to undefined behaviour. However, explicitly typing std::ref(a) warns you against passing a by reference, whereas typing just a does not.Troublous
Having to write std::ref(a) (instead of just a) to pass a by reference prevents you from accidentally thinking that a is being passed by value.Troublous
Even if we use std::ref, there would be a dangling reference in the above example, so nothing changes, right? So, you mean, using std::ref is slightly better because it explicitly shows that it's using a reference?Coady
@starriet: There are similar circumstances in which it would be safe to pass the reference (e.g. if the thread would be join-ed before the reference went out of scope, or the thread is being passed a reference to data with static storage duration). Using std::ref is asserting this is one of those situations. It's ensuring someone made a choice to use those semantics, rather than doing so by accident. It can't stop you from creating undefined behavior, but it can protect you from accidentally doing so.Succession
M
11

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); });
Mercerize answered 10/3, 2021 at 10:22 Comment(0)
J
0

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;
}
Jonahjonas answered 3/11, 2020 at 1:26 Comment(1)
There's no benefit to passing by pointer here; if the pointer is allocated live as shown, then you may as well have just passed by value (it's not like anything else will share the pointer here, so no thread communication is possible). Either pass by value (when sharing not needed), by reference (when sharing needed and the value is guaranteed to outlive its use in the thread), or by shared_ptr (when you need to share and have no idea which thread will stop using it first). Passing by raw pointer with the thread assuming ownership may as well just be pass by value.Succession

© 2022 - 2025 — McMap. All rights reserved.