Title
Further incorrect usages of result_of
Status
c++14
Section
[func.bind.bind] [futures.overview] [futures.async]
Submitter
Daniel Krügler

Created on 2010-12-07.00:00:00 last changed 123 months ago

Messages

Date: 2011-08-16.10:45:53

Proposed resolution:

The suggested wording changes are against the FDIS.

  1. Change [func.bind.bind] p. 3 as indicated:

    template<class F, class... BoundArgs>
      unspecified bind(F&& f, BoundArgs&&... bound_args);
    

    -2- Requires: is_constructible<FD, F>::value shall be true. For each Ti in BoundArgs, is_constructible<TiD, Ti>::value shall be true. INVOKE(fd, w1, w2, ..., wN) (20.8.2) shall be a valid expression for some values w1, w2, ..., wN, where N == sizeof...(bound_args).

    -3- Returns: A forwarding call wrapper g with a weak result type (20.8.2). The effect of g(u1, u2, ..., uM) shall be INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN), result_of<FD cv & (V1, V2, ..., VN)>::type), where cv represents the cv-qualifiers of g and the values and types of the bound arguments v1, v2, ..., vN are determined as specified below. […]

  2. Change [func.bind.bind] p. 7 as indicated:

    template<class R, class F, class... BoundArgs>
       unspecified bind(F&& f, BoundArgs&&... bound_args);
    

    -6- Requires: is_constructible<FD, F>::value shall be true. For each Ti in BoundArgs, is_constructible<TiD, Ti>::value shall be true. INVOKE(fd, w1, w2, ..., wN) shall be a valid expression for some values w1, w2, ..., wN, where N == sizeof...(bound_args).

    -7- Returns: A forwarding call wrapper g with a nested type result_type defined as a synonym for R. The effect of g(u1, u2, ..., uM) shall be INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN), R), where the values and types of the bound arguments v1, v2, ..., vN are determined as specified below. […]

  3. Change [func.bind.bind] p. 10 as indicated:

    -10- The values of the bound arguments v1, v2, ..., vN and their corresponding types V1, V2, ..., VN depend on the types TiD derived from the call to bind and the cv-qualifiers cv of the call wrapper g as follows:

    • if TiD is reference_wrapper<T>, the argument is tid.get() and its type Vi is T&;
    • if the value of is_bind_expression<TiD>::value is true, the argument is tid(std::forward<Uj>(uj)...) and its type Vi is result_of<TiD cv & (Uj&&...)>::type&&;
    • if the value j of is_placeholder<TiD>::value is not zero, the argument is std::forward<Uj>(uj) and its type Vi is Uj&&;
    • otherwise, the value is tid and its type Vi is TiD cv &.
  4. This resolution assumes that the wording of [futures.async] is intended to provide rvalues as arguments of INVOKE.

    Change the function signatures in header <future> synopsis [futures.overview] p. 1 and in [futures.async] p. 1 as indicated:

    template <class F, class... Args>
    future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
    async(F&& f, Args&&... args);
    template <class F, class... Args>
    future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
    async(launch policy, F&& f, Args&&... args);
    
  5. Change [futures.async] as indicated: (Remark: There is also a tiny editorial correction in p. 4 that completes one :: scope specifier)

    -3- Effects: […]

    • […]
    • if policy & launch::deferred is non-zero — Stores DECAY_COPY(std::forward<F>(f)) and DECAY_COPY(std::forward<Args>(args))... in the shared state. These copies of f and args constitute a deferred function. Invocation of the deferred function evaluates INVOKE(std::move(g), std::move(xyz)) where g is the stored value of DECAY_COPY(std::forward<F>(f)) and xyz is the stored copy of DECAY_COPY(std::forward<Args>(args)).... The shared state is not made ready until the function has completed. The first call to a non-timed waiting function (30.6.4) on an asynchronous return object referring to this shared state shall invoke the deferred function in the thread that called the waiting function. Once evaluation of INVOKE(std::move(g), std::move(xyz)) begins, the function is no longer considered deferred. [ Note: If this policy is specified together with other policies, such as when using a policy value of launch::async | launch::deferred, implementations should defer invocation or the selection of the policy when no more concurrency can be effectively exploited. — end note ]

    -4- Returns: an object of type future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type> that refers to the associated asynchronous state created by this call to async.

Date: 2011-08-16.10:45:53

[ Bloomington, 2011 ]

Move to Ready

Date: 2011-06-13.00:00:00

[ 2011-06-13: Daniel comments and refines the proposed wording changes ]

The feedback obtained following message c++std-lib-30745 and follow-ups point to the intention, that the implied provision of lvalues due to named variables in async should be provided as rvalues to support move-only types, but the functor type should be forwarded as lvalue in bind.

If bind were newly invented, the value strategy could be improved, because now we have a preference of ref & qualified function call operator overloads. But such a change seems to be too late now. User-code that needs to bind a callable object with an ref && qualified function call operator (or conversion function to function pointer) needs to use a corresponding wrapper similar to reference_wrapper that forwards the reference as rvalue-reference instead.

The wording has been adapted to honor these observations and to fit to FDIS numbering as well.

Date: 2010-12-07.00:00:00

Issue 2017 points out some incorrect usages of result_of in the declaration of the function call operator overload of reference_wrapper, but there are more such specification defects:

  1. According to [func.bind.bind] p. 3:

    [..] The effect of g(u1, u2, ..., uM) shall be INVOKE(fd, v1, v2, ..., vN, result_of<FD cv (V1, V2, ..., VN)>::type) [..]

    but fd is defined as "an lvalue of type FD constructed from std::forward<F>(f)". This means that the above usage must refer to result_of<FD cv & (V1, V2, ..., VN)> instead.

  2. Similar in [func.bind.bind] p. 10 bullet 2 we have:

    if the value of is_bind_expression<TiD>::value is true, the argument is tid(std::forward<Uj>(uj)...) and its type Vi is result_of<TiD cv (Uj...)>::type

    Again, tid is defined as "lvalue of type TiD constructed from std::forward<Ti>(ti)". This means that the above usage must refer to result_of<TiD cv & (Uj...)> instead. We also have similar defect as in 2017 in regard to the argument types, this leads us to the further corrected form result_of<TiD cv & (Uj&&...)>. This is not the end: Since the Vi are similar sensitive to the argument problem, the last part must say:

    "[..] its type Vi is result_of<TiD cv & (Uj&&...)>::type &&"

    (The bound arguments Vi can never be void types, therefore we don't need to use the more defensive std::add_rvalue_reference type trait)

  3. The function template async is declared as follows (the other overload has the same problem):

    template <class F, class... Args>
    future<typename result_of<F(Args...)>::type>
    async(F&& f, Args&&... args);
    

    This usage has the some same problems as we have found in reference_wrapper (2017) and more: According to the specification in [futures.async] the effective result type is that of the call of

    INVOKE(decay_copy(std::forward<F>(f)), decay_copy(std::forward<Args>(args))...)
    

    First, decay_copy potentially modifies the effective types to decay<F>::type and decay<Args>::type.... Second, the current specification is not really clear, what the value category of callable type or the arguments shall be: According to the second bullet of [futures.async] p. 3:

    Invocation of the deferred function evaluates INVOKE(g, xyz) where g is the stored value of decay_copy(std::forward<F>(f)) and xyz is the stored copy of decay_copy(std::forward<Args>(args))....

    This seems to imply that lvalues are provided in contrast to the direct call expression of [futures.async] p. 2 which implies rvalues instead. The specification needs to be clarified.

History
Date User Action Args
2014-02-20 13:20:35adminsetstatus: wp -> c++14
2012-02-12 18:36:43adminsetstatus: voting -> wp
2012-02-09 04:07:48adminsetstatus: ready -> voting
2011-08-16 10:45:53adminsetmessages: + msg5834
2011-08-16 10:45:53adminsetstatus: review -> ready
2011-06-13 15:04:23adminsetmessages: + msg5818
2011-03-24 16:58:37adminsetstatus: new -> review
2010-12-07 23:41:35adminsetmessages: + msg5466
2010-12-07 00:00:00admincreate