Usually, we will encounter some compiling problems, when implementing cross-platform c++ codes on Visual Studio. Codes compile without errors on windows, while other platform not, this time is about arguments passing of std thread constructor
Let's check these simple codes
#include <cstdio>
#include <thread>
struct A
{
int i;
};
void add_one(A & r)
{
++r.i;
printf("now i is %d\n", r.i);
}
int main()
{
A a{ 9 };
printf("i is %d\n", a.i);
std::thread t(add_one, a);
t.join();
printf("i after thread: %d", a.i);
return 0;
}
this program could compile and run on MSVC, we can get the following result:
i is 9
now i is 10
i after thread: 9
Obviously, the MSVC implementation passes a copied a to the new-created thread,
which is also the reason why a is not modified after threads end. But this program
does not compile on g++, because add_one
accepts reference arguments, while we
passed a value, the corresponding invoke implementation will not be found.
Different g++ compiler will show different error for this issue:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/thread:342:5: error:
attempt to use a deleted function
__invoke(_VSTD::move(_VSTD::get<1>(__t)), _VSTD::move(_VSTD::get<_In...
^
This should be caused by matching the null implementation of invoke:
__nat __invoke(__any, ...);
/usr/include/c++/4.8.2/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.2/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’
_M_invoke(_Index_tuple<_Indices...>)
No type named 'type' in result_type, so gcc does not handle arguments of this type
There's 2 ways to solve this issue:
add_one
arguments to value, this will output the same result with MSVCstd::thread t(add_one, std::ref(a));
this will output the following result:i is 9
now i is 10
i after thread: 10
MSVC DID prevent reference arguments, in some way. If we remove the struct to
basic type int
, the program will be look like this:
#include <cstdio>
#include <thread>
void add_one(int & r)
{
++r;
printf("now i is %d\n", r);
}
int main()
{
int i = 9;
printf("i is %d\n", i);
std::thread t(add_one, i);
t.join();
printf("i after thread: %d\n", i);
return 0;
}
Then MSVC will recognize this:
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(240): error C2672: “std::invoke”: 未找到匹配的重载函数
It looks like that MSVC does not implement this strictly, the reason is not known. But what if we pass reference, let's change the codes to:
#include <cstdio>
#include <thread>
struct A
{
int i;
};
void add_one(A & r)
{
++(r.i);
printf("now i is %d\n", r.i);
}
int main()
{
A a{ 9 };
A & r = a;
printf("i is %d\n", a.i);
std::thread t(add_one, r);
t.join();
printf("i after thread: %d\n", a.i);
return 0;
}
i
is still 9 in the end, and these codes don't compile on g++, by the way,
but if we are passing pointers:
#include <cstdio>
#include <thread>
struct A
{
int i;
};
void add_one(A * r)
{
++(r->i);
printf("now i is %d\n", r->i);
}
int main()
{
A a{ 9 };
A * r = &a;
printf("i is %d\n", a.i);
std::thread t(add_one, r);
t.join();
printf("i after thread: %d\n", a.i);
return 0;
}
i will be 10 finally, and g++ compiles too, result is also the same.
std::ref must be used if we're running a thread using reference, or unexpected result will be produced on MSVC, though it compiles