Title
packaged_task constructors should be constrained
Status
c++14
Section
[futures.task.members]
Submitter
Jonathan Wakely

Created on 2011-11-02.00:00:00 last changed 123 months ago

Messages

Date: 2013-09-26.11:12:18

Proposed resolution:

This wording is relative to the FDIS.

  1. Insert a new Remarks element to [thread.thread.constr] around p3 as indicated:

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

    -3- Requires: F and each Ti in Args shall satisfy the MoveConstructible requirements. INVOKE(DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) (20.8.2) shall be a valid expression.

    -?- Remarks: This constructor shall not participate in overload resolution if decay<F>::type is the same type as std::thread.

  2. Insert a new Remarks element to [futures.task.members] around p2 as indicated:

    template <class F>
      packaged_task(F&& f);
    template <class F, class Allocator>
      explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f);
    

    -2- Requires: INVOKE(f, t1, t2, ..., tN, R), where t1, t2, ..., tN are values of the corresponding types in ArgTypes..., shall be a valid expression. Invoking a copy of f shall behave the same as invoking f.

    -?- Remarks: These constructors shall not participate in overload resolution if decay<F>::type is the same type as std::packaged_task<R(ArgTypes...)>.

Date: 2013-09-29.11:37:54

[ 2013, Chicago ]

Move to Immediate resolution.

Howard volunteered existing implementation experience with the first change, and saw no issue that the second would introduce any new issue.

Date: 2012-11-02.22:48:46

[ 2012, Portland ]

This issue appears to be more about library specification than technical concurrency issues, so should be handled in LWG.

Date: 2011-11-28.18:38:06

With the proposed resolution of 2067, this no longer selects the copy constructor:

std::packaged_task<void()> p1;
std::packaged_task<void()> p2(p1);

Instead this constructor is a better match:

template <class F>
 explicit packaged_task(F&& f);

This attempts to package a packaged_task, which internally tries to copy p2, which fails because the copy constructor is deleted. For at least one implementation the resulting error message is much less helpful than the expected "cannot call deleted function" because it happens after instantiating several more templates rather than in the context where the constructor is called.

I believe the solution is to constrain to the template constructors so the template argument F cannot be deduced as (possibly cv) packaged_task& or packaged_task. It could be argued this constraint is already implied because packaged_task is not copyable and the template constructors require that "invoking a copy of f shall behave the same as invoking f".

Daniel points out that the variadic constructor of std::thread described in [thread.thread.constr] has a similar problem and suggests a similar wording change, which has been integrated below.

An alternative is to declare thread(thread&) and packaged_task(packaged_task&) as deleted.

History
Date User Action Args
2014-02-20 13:20:35adminsetstatus: wp -> c++14
2013-09-29 11:37:54adminsetstatus: immediate -> wp
2013-09-26 11:12:18adminsetmessages: + msg6625
2013-09-26 11:12:18adminsetstatus: new -> immediate
2012-11-02 22:48:46adminsetmessages: + msg6237
2011-11-27 21:58:29adminsetmessages: + msg5924
2011-11-02 00:00:00admincreate