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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With