Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Details in the process of constructing a std::thread object

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?

like image 298
Lingxi Avatar asked Apr 17 '15 08:04

Lingxi


1 Answers

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 executes INVOKE(DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) with the calls to DECAY_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 of f will be thrown in the constructing thread, not the new thread. —end note ] If the invocation of INVOKE(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 function decay_copy(x) and use the result, where decay_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.

like image 61
Angew is no longer proud of SO Avatar answered Sep 22 '22 09:09

Angew is no longer proud of SO