This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofC++11 status.
Section: 32.4.3.3[thread.thread.constr]Status:C++11Submitter: Anthony WilliamsOpened: 2008-10-23Last modified: 2016-01-28
Priority:Not Prioritized
View otheractive issues in [thread.thread.constr].
View all otherissues in [thread.thread.constr].
View all issues withC++11 status.
Discussion:
Addresses UK 323
Thethread constructor for starting a new thread with a function andarguments is overly constrained by the signature requiring rvaluereferences forfunc andargs and theCopyConstructible requirementsfor the elements ofargs. The use of an rvalue reference for thefunction restricts the potential use of a plain function name, sincethe type of the bound parameter will be deduced to be a functionreference and decay to pointer-to-function will not happen. Thistherefore complicates the implementation in order to handle a simplecase. Furthermore, the use of rvalue references for args prevents thearray to pointer decay. Since arrays are notCopyConstructible or evenMoveConstructible, this essentially prevents the passing of arrays asparameters. 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 isconst char[6].
By changing the signature to take all parameters by value we caneliminate theCopyConstructible requirement and permit the use ofarrays, as the parameter passing semantics will cause the necessaryarray-to-pointer decay. They will also cause the function name todecay to a pointer to function and allow the implementation to handlefunctions and function objects identically.
The new signature of thethread constructor for a function andarguments is thus:
template<typename F,typename... Args>thread(F,Args... args);
Since the parameter packArgs can be empty, the single-parameterconstructor that takes just a function by value is now redundant.
[Howard adds:]
I agree with everything Anthony says in this issue. However I believe wecan optimize in such a way as to get the pass-by-value behavior with thepass-by-rvalue-ref performance. The performance difference is that the latterremoves a
movewhen 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 toget pointer decay, and then changed to pass by rvalue reference, but modified withdecay<T>to retain the pass-by-value behavior. If we were toapply 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:
Fand eachTiinArgsshall beCopyConstructibleif an lvalue and otherwiseMoveConstructible.INVOKE(f, w1, w2, ..., wN)(22.10.4[func.require]) shall be a valid expression forsome valuesw1, w2, ... , wN,whereN == sizeof...(Args).-5-Effects: Constructs an object of type
threadand executes.Constructsthe following objects in memory which is accessible to a new thread of executionas if:INVOKE(f, t1, t2, ..., tN)in a newthread of execution, wheret1, t2, ..., tNare the values inargs...typename decay<F>::type g(std::forward<F>(f));tuple<typename decay<Args>::type...> w(std::forward<Args>(args)...);The new thread ofexecution executes
INVOKE(g, wi...)where thewi...refersto the elements stored in thetuple w.Any return value fromgis ignored.IfIf the evaluation offterminates with an uncaught exception,std::terminate()shall be called.INVOKE(g, wi...)terminateswith an uncaught exception,std::terminate()shall be called [Note:std::terminate()could be called before enteringg. --end note]. Anyexception thrown before the evaluation ofINVOKEhas started shall becatchable 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 rewordedsince "catchable" is not a term of art (and is used nowhere else).
[2009-07 Frankfurt:]
This is linked toN2901.
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 via1176(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::threadin 32.4.3[thread.thread.class] to remove thefollowing signature:template<class F> explicit thread(F f);template<class F, class ... Args>explicit thread(F&& f, Args&& ... args);Modify 32.4.3.3[thread.thread.constr] to replace the constructors prior to paragraph 4 withthe single constructor as above. Replace paragraph 4 - 6 with thefollowing:
-4-Requires:
Fand eachTiinArgsshall beCopyConstructibleif an lvalue and otherwiseMoveConstructible.INVOKE(f, w1, w2, ..., wN)(22.10.4[func.require]) shall be a valid expression forsome valuesw1, w2, ... , wN,whereN == sizeof...(Args).-5-Effects: Constructs an object of type
threadand executes.Constructsthe following objects:INVOKE(f, t1, t2, ..., tN)in a newthread of execution, wheret1, t2, ..., tNare the values inargs...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 fromgis ignored.IfIf the evaluation offterminates with an uncaught exception,std::terminate()shall be called.INVOKE(g, wi...)terminateswith an uncaught exception,std::terminate()shall be called [Note:std::terminate()could be called before enteringg. --end note]. Anyexception thrown before the evaluation ofINVOKEhas started shall becatchable in the calling thread.-6-Synchronization: The invocation of the constructorhappens before theinvocation of
fg.
[2010-01-19 Moved to Tentatively Ready after 5 positive votes on c++std-lib.]
Proposed resolution:
Modify the class definition ofstd::thread in 32.4.3[thread.thread.class] to remove thefollowing signature:
template<class F> explicit thread(F f);template<class F, class ... Args>explicit thread(F&& f, Args&& ... args);
Modify 32.4.3.3[thread.thread.constr] to replace the constructors prior to paragraph 4 withthe single constructor as above. Replace paragraph 4 - 6 with thefollowing:
Given a function as follows:
template<typename T> typename decay<T>::type decay_copy(T&& v) { return std::forward<T>(v); }-4-Requires:
Fand eachTiinArgsshallbesatisfytheCopyConstructibleif an lvalue and otherwiseMoveConstructiblerequirements.INVOKE(f, w1, w2, ..., wN)(22.10.4[func.require])shall be a valid expression for some valuesw1, w2, ... , wN,whereN == 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
threadand executesThe new thread of execution executesINVOKE(f, t1, t2, ..., tN)in a new thread of execution, wheret1, t2, ..., tNare the values inargs.... Any returnvalue fromfis ignored. Iffterminates with anuncaught exception,std::terminate()shall be called.INVOKE(decay_copy(std::forward<F>(f)),decay_copy(std::forward<Args>(args))...)with the calls todecay_copy()being evaluated inthe constructing thread. Any return value from this invocation isignored. [Note: this implies any exceptions not thrown from theinvocation of the copy offwill 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 uncaughtexception,std::terminateshall be called.-6-Synchronization: The invocation of the constructorhappens before theinvocation ofthe copy of
f.