I am passing a private variable from a class function to a thread executing a function not part of the class. The function call works without problems when executed normally, but when I try to execute it using a separate thread, I get the error message "static assertion failed".
Does anyone know what I'm doing wrong here?
I've added an example program below, which calls a function (do_something()
) from a class (A
) that launches a thread (th1
) with a non-member function (outside()
).
#include <iostream>
#include <vector>
#include <thread>
void outside(std::vector<int>& array) {
for (int i = 0; i < array.size(); i ) {
printf("array[%d] = %d\n", i, array[i]);
}
}
class A {
public:
void do_something();
private:
std::vector<int> array = { 3, 4, 5 };
};
void A::do_something() {
outside(this->array); // This works.
std::thread th1(outside, this->array); // This doesn't work.
th1.join();
}
int main() {
A example;
example.do_something();
return 0;
}
The compilation of this program results in the following error message:
In file included from main.cpp:3:
/usr/include/c /9/thread: In instantiation of ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(std::vector<int>&); _Args = {A*, std::vector<int, std::allocator<int> >&}; <template-parameter-1-3> = void]’:
<span onclick="ide.gotoLine('main.cpp',20)">main.cpp:20:47</span>: required from here
/usr/include/c /9/thread:120:44: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues
120 | typename decay<_Args>::type...>::value,
| ^~~~~
/usr/include/c /9/thread: In instantiation of ‘struct std::thread::_Invoker<std::tuple<void (*)(std::vector<int, std::allocator<int> >&), A*, std::vector<int, std::allocator<int> > > >’:
/usr/include/c /9/thread:131:22: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(std::vector<int>&); _Args = {A*, std::vector<int, std::allocator<int> >&}; <template-parameter-1-3> = void]’
<span onclick="ide.gotoLine('main.cpp',20)">main.cpp:20:47</span>: required from here
/usr/include/c /9/thread:243:4: error: no type named ‘type’ in ‘struct std::thread::_Invoker >&), A*, std::vector > > >::__result >&), A*, std::vector > > >’
243 | _M_invoke(_Index_tuple<_Ind...>)
| ^~~~~~~~~
/usr/include/c /9/thread:247:2: error: no type named ‘type’ in ‘struct std::thread::_Invoker >&), A*, std::vector > > >::__result >&), A*, std::vector > > >’
247 | operator()()
| ^~~~~~~~
CodePudding user response:
All values passed to the constructor of std::thread
are moved or copied.
You can find this in the documentation.
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).
If you think about the call of the constructor here
std::thread th1(outside, this->array);
there is no way to be explicit about if you want the array to be passed by value or by reference here. You could argue that it should look at the function signature and default to whatever the signature does, but that could potentially be confusing as well.
Seems like the decision was made to always assume copy/move, if you want to pass by ref you need to be explicit about it with std::ref
/std::cref
.