Reputation: 14977
I'm interested in (and confused about) the details of constructing a std::thread
object. According to cppreference, both the thread function and all arguments are value-copied to some thread-accessible storage, and then invoke.
1) What exactly is this thread-accessible storage? Is it semantically equivalent to some kind of thread-local storage, and the variables are destructed after the thread function returned?
2) What is the value-category of the arguments when passed to the thread function? The description on cppreference suggests that they are passed as l-values (they are given names anyway). My tests on GCC and clang seem to suggest the opposite, i.e., r-values. Specifically, the following code does not compile:
void f(int& a) {
std::cout << ++a << '\n';
}
int main() {
std::thread t(&f, 1);
t.join();
return 0;
}
It compiles if we change f
to
void f(int&& a) {
std::cout << ++a << '\n';
}
int main() {
std::thread t(&f, 1);
t.join();
return 0;
}
So, what does the standard say about this?
Upvotes: 6
Views: 1273
Reputation: 171127
1) This "thread-accessible storage" bit of text is not represented directly in the standard. The standard simply says that the function is invoked with arguments obtained by decay_copy
.
2) If you study decay_copy
closely, you will find that it returns by value (because its return type is std::decay
of something). So the function f
is called with rvalue arguments (prvalue arguments, in fact).
If you want to pass lvalues (references), you can use std::ref
and std::cref
to wrap them.
The exact quote, C++11 30.3.1.2/4:
Effects: Constructs an object of type
thread
. The new thread of execution executesINVOKE(DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)
with the calls toDECAY_COPY
being evaluated in the constructing thread. Any return value from this invocation is ignored. [ Note: This implies that any exceptions not thrown from the invocation of the copy off
will be thrown in the constructing thread, not the new thread. —end note ] If the invocation ofINVOKE(DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)
terminates with an uncaught exception,std::terminate
shall be called.
DECAY_COPY
is defined in 30.2.6/1:
In several places in this Clause the operation
DECAY_COPY(x)
is used. All such uses mean call the functiondecay_copy(x)
and use the result, wheredecay_copy
is defined as follows:template <class T> typename decay<T>::type decay_copy(T&& v) { return std::forward<T>(v); }
INVOKE
is defined in 20.8.2 pretty much in the same way as cppreference describes the invocation in the link you've provided.
Upvotes: 3