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.
Section: 32.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 withdecay<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 eachTi
inArgs
shall beCopyConstructible
if an lvalue and otherwiseMoveConstructible
.INVOKE(f, w1, w2, ..., wN)
(22.10.4 [func.require]) shall be a valid expression for some valuesw1, w2, ... , wN,
whereN == sizeof...(Args)
.-5- Effects: Constructs an object of type
thread
and executes.INVOKE(f, t1, t2, ..., tN)
in a new thread of execution, wheret1, t2, ..., tN
are the values inargs...
Any return value from
g
is ignored.Iff
terminates with an uncaught exception,std::terminate()
shall be called.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(i)).
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(i) 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 32.4.3 [thread.thread.class] to remove the following signature:template<class F> explicit thread(F f);template<class F, class ... Args> thread(F&& f, Args&& ... args);Modify 32.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 eachTi
inArgs
shall beCopyConstructible
if an lvalue and otherwiseMoveConstructible
.INVOKE(f, w1, w2, ..., wN)
(22.10.4 [func.require]) shall be a valid expression for some valuesw1, w2, ... , wN,
whereN == sizeof...(Args)
.-5- Effects: Constructs an object of type
thread
and executes.INVOKE(f, t1, t2, ..., tN)
in a new thread of execution, wheret1, t2, ..., tN
are the values inargs...
Any return value from
g
is ignored.Iff
terminates with an uncaught exception,std::terminate()
shall be called.-6- Synchronization: The invocation of the constructor happens before the invocation of
.f
[ 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 32.4.3 [thread.thread.class] to remove the
following signature:
template<class F> explicit thread(F f);template<class F, class ... Args> thread(F&& f, Args&& ... args);
Modify 32.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 eachTi
inArgs
shallbeCopyConstructible
if an lvalue and otherwiseMoveConstructible
.INVOKE(f, w1, w2, ..., wN)
(22.10.4 [func.require]) shall be a valid expression for some valuesw1, w2, ... , wN,
whereN == sizeof...(Args)
.-5- Effects: Constructs an object of type
thread
and executesINVOKE(f, t1, t2, ..., tN)
in a new thread of execution, wheret1, t2, ..., tN
are the values inargs...
. Any return value fromf
is ignored. Iff
terminates with an uncaught exception,std::terminate()
shall be called.-6- Synchronization: The invocation of the constructor happens before the invocation of
f
.