Summary: how to properly pass references to the std::thread
constructor via std::ref()
and std::cref()
wrappers
Wrap reference parameters with std::ref()
and const
reference parameters with std::cref()
:
// Function prototypes
void foo(Data& data); // takes a reference parameter
void foo2(const Data& data); // takes a const reference parameter
// std::thread constructor calls with proper `std::ref()` and `std::cref()`
// wrappers
std::thread t1 = std::thread(foo, std::ref(data));
std::thread t2 = std::thread(foo2, std::cref(data));
Problem
I also couldn't seem to get reference parameters to work with the std::thread()
constructor. I was about to make this a separate Q&A but discovered this question here instead. I'd like to post the full error output from g++ below to make this question more easily searchable.
My example:
#include <cstdint> // For `uint8_t`, `int8_t`, etc.
#include <cstdio> // For `printf()`
#include <iostream> // For `std::cin`, `std::cout`, `std::endl`, etc.
#include <thread>
struct Data
{
int i = 7;
};
// Accepts a reference
void foo(Data& data)
{
printf("data.i = %i\n", data.i);
}
int main()
{
printf("`std::thread` test\n");
Data data;
std::thread t1 = std::thread(foo, data); // <========= here
t1.join();
return 0;
}
Sample build command and output:
eRCaGuy_hello_world/cpp$ g++ -Wall -Wextra -Werror -O3 -std=c++17 -pthread std_thread__pass_parameter_by_reference_to_constructor.cpp -o bin/a && bin/a
In file included from std_thread__pass_parameter_by_reference_to_constructor.cpp:87:
/usr/include/c++/8/thread: In instantiation of ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(Data&); _Args = {Data&}; <template-parameter-1-3> = void]’:
std_thread__pass_parameter_by_reference_to_constructor.cpp:105:43: required from here
/usr/include/c++/8/thread:120:17: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues
static_assert( __is_invocable<typename decay<_Callable>::type,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typename decay<_Args>::type...>::value,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/8/thread: In instantiation of ‘struct std::thread::_Invoker<std::tuple<void (*)(Data&), Data> >’:
/usr/include/c++/8/thread:132:22: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(Data&); _Args = {Data&}; <template-parameter-1-3> = void]’
std_thread__pass_parameter_by_reference_to_constructor.cpp:105:43: required from here
/usr/include/c++/8/thread:250:2: error: no matching function for call to ‘std::thread::_Invoker<std::tuple<void (*)(Data&), Data> >::_M_invoke(std::thread::_Invoker<std::tuple<void (*)(Data&), Data> >::_Indices)’
operator()()
^~~~~~~~
/usr/include/c++/8/thread:241:4: note: candidate: ‘template<long unsigned int ..._Ind> decltype (std::__invoke((_S_declval<_Ind>)()...)) std::thread::_Invoker<_Tuple>::_M_invoke(std::_Index_tuple<_Ind ...>) [with long unsigned int ..._Ind = {_Ind ...}; _Tuple = std::tuple<void (*)(Data&), Data>]’
_M_invoke(_Index_tuple<_Ind...>)
^~~~~~~~~
/usr/include/c++/8/thread:241:4: note: template argument deduction/substitution failed:
/usr/include/c++/8/thread: In substitution of ‘template<long unsigned int ..._Ind> decltype (std::__invoke(_S_declval<_Ind>()...)) std::thread::_Invoker<std::tuple<void (*)(Data&), Data> >::_M_invoke<_Ind ...>(std::_Index_tuple<_Ind ...>) [with long unsigned int ..._Ind = {0, 1}]’:
/usr/include/c++/8/thread:250:2: required from ‘struct std::thread::_Invoker<std::tuple<void (*)(Data&), Data> >’
/usr/include/c++/8/thread:132:22: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(Data&); _Args = {Data&}; <template-parameter-1-3> = void]’
std_thread__pass_parameter_by_reference_to_constructor.cpp:105:43: required from here
/usr/include/c++/8/thread:243:29: error: no matching function for call to ‘__invoke(std::__tuple_element_t<0, std::tuple<void (*)(Data&), Data> >, std::__tuple_element_t<1, std::tuple<void (*)(Data&), Data> >)’
-> decltype(std::__invoke(_S_declval<_Ind>()...))
~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/8/tuple:41,
from /usr/include/c++/8/bits/unique_ptr.h:37,
from /usr/include/c++/8/memory:80,
from /usr/include/c++/8/thread:39,
from std_thread__pass_parameter_by_reference_to_constructor.cpp:87:
/usr/include/c++/8/bits/invoke.h:89:5: note: candidate: ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...)’
__invoke(_Callable&& __fn, _Args&&... __args)
^~~~~~~~
/usr/include/c++/8/bits/invoke.h:89:5: note: template argument deduction/substitution failed:
/usr/include/c++/8/bits/invoke.h: In substitution of ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = void (*)(Data&); _Args = {Data}]’:
/usr/include/c++/8/thread:243:29: required by substitution of ‘template<long unsigned int ..._Ind> decltype (std::__invoke(_S_declval<_Ind>()...)) std::thread::_Invoker<std::tuple<void (*)(Data&), Data> >::_M_invoke<_Ind ...>(std::_Index_tuple<_Ind ...>) [with long unsigned int ..._Ind = {0, 1}]’
/usr/include/c++/8/thread:250:2: required from ‘struct std::thread::_Invoker<std::tuple<void (*)(Data&), Data> >’
/usr/include/c++/8/thread:132:22: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(Data&); _Args = {Data&}; <template-parameter-1-3> = void]’
std_thread__pass_parameter_by_reference_to_constructor.cpp:105:43: required from here
/usr/include/c++/8/bits/invoke.h:89:5: error: no type named ‘type’ in ‘struct std::__invoke_result<void (*)(Data&), Data>’
There's a lot to look at there, but the first part of the error that stuck out to me was:
/usr/include/c++/8/thread:120:17: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues
What's the problem?
Solution
You can't pass references directly to the std::thread()
constructor. You must wrap them in std::ref()
.
See the std::thread::thread()
constructor reference pg here: https://en.cppreference.com/w/cpp/thread/thread/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
).
So, wrap reference parameters with std::ref()
and const
reference parameters with std::cref()
, like this:
std::thread t1 = std::thread(foo, std::ref(data));
std::thread t2 = std::thread(foo2, std::cref(data));
Full example:
std_thread__pass_parameter_by_reference_to_constructor_via_std_ref.cpp:
// C & C++ includes
#include <cstdint> // For `uint8_t`, `int8_t`, etc.
#include <cstdio> // For `printf()`
#include <iostream> // For `std::cin`, `std::cout`, `std::endl`, etc.
#include <thread>
struct Data
{
int i = 7;
};
// Accepts a reference
void foo(Data& data)
{
printf("data.i = %i\n", data.i);
}
// Accepts a const reference
void foo2(const Data& data)
{
printf("data.i = %i\n", data.i);
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("`std::thread` test\n");
Data data;
std::thread t1 = std::thread(foo, std::ref(data));
std::thread t2 = std::thread(foo2, std::cref(data));
t1.join();
t2.join();
return 0;
}
References
- Where I first learned this: C++11 std::thread accepting function with rvalue parameter; see my comments under this answer too
- https://en.cppreference.com/w/cpp/thread/thread/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
).