We know how to construct a thread without any input arguments. But a function printNTimes(int n, const std::string& msg) requires two arguments. How to run it in a separate thread is described below.

std::string msg = "Today is a beautiful day.";
std::thread t(printNTimes, 5, msg);

The code runs printNTimes with arguments 5 and msg in a new thread. But something unexpected happens if you don’t have a const reference. Let us consider a referenceArguments.cpp below

#include <iostream>
#include <thread>
#include <algorithm>

void toUpper(std::string& str)
{
    std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}

int main()
{
    std::string str = "Today is a beautiful day.";
    
    std::cout << "Before: " << str << std::endl;
    
    std::thread t(toUpper, str);
    t1.join();
    
    std::cout << "After: " << str << std::endl;

    return 0;
}

This looks perfectly reasonable, at least to me. But it doesn’t compile.

$ g++ referenceArguments.cpp -std=c++11
In file included from /usr/include/c++/4.8/thread:39:0,
                 from referenceArguments.cpp:2:
/usr/include/c++/4.8/functional: In instantiation of ‘struct std::_Bind_simple<void (*(std::basic_string<char>))(std::basic_string<char>&)>’:
/usr/include/c++/4.8/thread:137:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(std::basic_string<char>&); _Args = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >&}]’
referenceArguments.cpp:16:32:   required from here
/usr/include/c++/4.8/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<void (*(std::basic_string<char>))(std::basic_string<char>&)>’
       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 (*(std::basic_string<char>))(std::basic_string<char>&)>’
         _M_invoke(_Index_tuple<_Indices...>)

The error message is quite cryptic. The solution is to add std::ref around str:

std::thread t(toUpper, std::ref(str));

Explanation

The reasoning behind it is that a thread, during a construction, takes a function and copies all of the arguments to its internal storage. Then, accordingly to the function signature, thread passes the copies to the function by values or references. If the function expects a reference, it gets a reference to an internal copy of the object.

In the example

std::thread t(printNTimes, 5, msg);

the thread t copies arguments 5 and msg to its internal storage. Then, the function printNTimes recieves a reference to the internal copy of the msg not a reference to the original msg.

If we use std::ref, the function correctly gets a reference to the original object, like in the example

std::thread t(toUpper, std::ref(str));

In the case of non const reference, we get a compiler error if we don’t use the std::ref, but in the case of const reference we don’t get an error . Therefore, I recommend using std::ref every time when we have references.

Another example

Here is arguments.cpp, which uses the printNTimes function from above.

#include <iostream>
#include <thread>

void printNTimes(int n, const std::string& msg)
{
    for (int i = 0; i != n; i++)
    {
        std::cout << msg << std::endl;
    }
}

int main()
{
    std::string msg1 = "Today is a beautiful day.";
    std::thread t1(printNTimes, 6, std::ref(msg1));
    
    std::string msg2 = "And sun is shining.";
    std::thread t2(printNTimes, 6, std::ref(msg2));
    
    t1.join();
    t2.join();
    
    return 0;
}

The output might be:

$ ./arguments 
Today is a beautiful day.And sun is shining.

Today is a beautiful day.And sun is shining.
And sun is shining.
And sun is shining.
And sun is shining.
And sun is shining.

Today is a beautiful day.
Today is a beautiful day.
Today is a beautiful day.
Today is a beautiful day.

It looks a bit strange. This is because of so called data races.

Summary

We learned how to pass an argument to a function, which will run in a separate thread. The std::ref is used for passing a reference to a variable.

In the next article, we will investigate why is the output of the final program strange – in concurrent language: we will explore data races.

Links: