This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++11 status.

### 929. Thread constructor

Section: 33.4.3.3 [thread.thread.constr] Status: C++11 Submitter: Anthony Williams Opened: 2008-10-23 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [thread.thread.constr].

View all issues with C++11 status.

Discussion:

Addresses UK 323

The `thread` constructor for starting a new thread with a function and arguments is overly constrained by the signature requiring rvalue references for `func` and `args` and the `CopyConstructible` requirements for the elements of `args`. The use of an rvalue reference for the function restricts the potential use of a plain function name, since the type of the bound parameter will be deduced to be a function reference and decay to pointer-to-function will not happen. This therefore complicates the implementation in order to handle a simple case. Furthermore, the use of rvalue references for args prevents the array to pointer decay. Since arrays are not `CopyConstructible` or even `MoveConstructible`, this essentially prevents the passing of arrays as parameters. In particular it prevents the passing of string literals. Consequently a simple case such as

>     void f(const char*);
>     std::thread t(f,"hello");

is ill-formed since the type of the string literal is `const char[6]`.

By changing the signature to take all parameters by value we can eliminate the `CopyConstructible` requirement and permit the use of arrays, as the parameter passing semantics will cause the necessary array-to-pointer decay. They will also cause the function name to decay to a pointer to function and allow the implementation to handle functions and function objects identically.

The new signature of the `thread` constructor for a function and arguments is thus:

>     template<typename F,typename... Args>
>     thread(F,Args... args);

Since the parameter pack `Args` can be empty, the single-parameter constructor that takes just a function by value is now redundant.

[ Howard adds: ]

> I agree with everything Anthony says in this issue. However I believe we can optimize in such a way as to get the pass-by-value behavior with the pass-by-rvalue-ref performance. The performance difference is that the latter removes a `move` when passing in an lvalue.

> This circumstance is very analogous to `make_pair` (22.3 [pairs]) where we started with passing by const reference, changed to pass by value to get pointer decay, and then changed to pass by rvalue reference, but modified with `decay<T>` to retain the pass-by-value behavior. If we were to apply the same solution here it would look like:

> >     template <class F> explicit thread(F f);
> >     template <class F, class ...Args> thread(F&& f, Args&&... args);

> > > -4- Requires: `F` and each `Ti` in `Args` shall be `CopyConstructible` if an lvalue and otherwise `MoveConstructible`. `INVOKE(f, w1, w2, ..., wN)` (22.10.4 [func.require]) shall be a valid expression for some values `w1, w2, ... , wN,` where `N == sizeof...(Args)`.

> > > -5- Effects: Constructs an object of type `thread` and executes `INVOKE(f, t1, t2, ..., tN)` in a new thread of execution, where `t1, t2, ..., tN` are the values in `args...`. Constructs the following objects in memory which is accessible to a new thread of execution as if:

> > > >     typename decay<F>::type g(std::forward<F>(f));
> > > >     tuple<typename decay<Args>::type...> w(std::forward<Args>(args)...);

> > > The new thread of execution executes `INVOKE(g, wi...)` where the `wi...` refers to the elements stored in the `tuple w`. Any return value from `g` is ignored. If `f` terminates with an uncaught exception, `std::terminate()` shall be called. If the evaluation of `INVOKE(g, wi...)` terminates with an uncaught exception, `std::terminate()` shall be called [Note: `std::terminate()` could be called before entering `g`. -- end note]. Any exception thrown before the evaluation of `INVOKE` has started shall be catchable in the calling thread.

> Text referring to when `terminate()` is called was contributed by Ganesh.

[ Batavia (2009-05): ]

> We agree with the proposed resolution, but would like the final sentence to be reworded since "catchable" is not a term of art (and is used nowhere else).

[ 2009-07 Frankfurt: ]

> This is linked to N2901.

> Howard to open a separate issue to remove (1176).

> In Frankfurt there is no consensus for removing the variadic constructor.

[ 2009-10 Santa Cruz: ]

> We want to move forward with this issue. If we later take it out via 1176 then that's ok too. Needs small group to improve wording.

[ 2009-10 Santa Cruz: ]

> Stefanus provided revised wording. Moved to Review Here is the original wording:

> > Modify the class definition of `std::thread` in 33.4.3 [thread.thread.class] to remove the following signature:

> > >     template<class F> explicit thread(F f);
> > >     template<class F, class ... Args> explicit thread(F&& f, Args&& ... args);

> > Modify 33.4.3.3 [thread.thread.constr] to replace the constructors prior to paragraph 4 with the single constructor as above. Replace paragraph 4 - 6 with the following:

> > > -4- Requires: `F` and each `Ti` in `Args` shall be `CopyConstructible` if an lvalue and otherwise `MoveConstructible`. `INVOKE(f, w1, w2, ..., wN)` (22.10.4 [func.require]) shall be a valid expression for some values `w1, w2, ... , wN,` where `N == sizeof...(Args)`.

> > > -5- Effects: Constructs an object of type `thread` and executes `INVOKE(f, t1, t2, ..., tN)` in a new thread of execution, where `t1, t2, ..., tN` are the values in `args...`. Constructs the following objects:

> > > >     typename decay<F>::type g(std::forward<F>(f));
> > > >     tuple<typename decay<Args>::type...> w(std::forward<Args>(args)...);

> > > and executes `INVOKE(g, wi...)` in a new thread of execution. These objects shall be destroyed when the new thread of execution completes. Any return value from `g` is ignored. If `f` terminates with an uncaught exception, `std::terminate()` shall be called. If the evaluation of `INVOKE(g, wi...)` terminates with an uncaught exception, `std::terminate()` shall be called [Note: `std::terminate()` could be called before entering `g`. -- end note]. Any exception thrown before the evaluation of `INVOKE` has started shall be catchable in the calling thread.

> > > -6- Synchronization: The invocation of the constructor happens before the invocation of `f` `g`.

[ 2010-01-19 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]

Proposed resolution:

Modify the class definition of `std::thread` in 33.4.3 [thread.thread.class] to remove the following signature:

>     template<class F> explicit thread(F f);
>     template<class F, class ... Args> explicit thread(F&& f, Args&& ... args);

Modify 33.4.3.3 [thread.thread.constr] to replace the constructors prior to paragraph 4 with the single constructor as above. Replace paragraph 4 - 6 with the following:

> Given a function as follows:

> >     template<typename T> typename decay<T>::type decay_copy(T&& v)
> >         { return std::forward<T>(v); }

> -4- Requires: `F` and each `Ti` in `Args` shall be `CopyConstructible` if an lvalue and otherwise satisfy the `MoveConstructible` requirements. `INVOKE(f, w1, w2, ..., wN)` (22.10.4 [func.require]) shall be a valid expression for some values `w1, w2, ... , wN,` where `N == sizeof...(Args)`. `INVOKE(decay_copy(std::forward<F>(f)), decay_copy(std::forward<Args>(args))...)` (22.10.4 [func.require]) shall be a valid expression.

> -5- Effects: Constructs an object of type `thread` and executes `INVOKE(f, t1, t2, ..., tN)` in a new thread of execution, where `t1, t2, ..., tN` are the values in `args...`. Any return value from `f` is ignored. If `f` terminates with an uncaught exception, `std::terminate()` shall be called. 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 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.

> -6- Synchronization: The invocation of the constructor happens before the invocation of the copy of `f`.