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

### 2021. Further incorrect usages of `result_of`

Section: 20.14.15.4 [func.bind.bind], 32.9.1 [futures.overview], 32.9.9 [futures.async] Status: C++14 Submitter: Daniel Krügler Opened: 2010-12-07 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [func.bind.bind].

View all issues with C++14 status.

Discussion:

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 20.14.15.4 [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 20.14.15.4 [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 32.9.9 [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 32.9.9 [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 32.9.9 [futures.async] p. 2 which implies rvalues instead. The specification needs to be clarified.

[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.

[Bloomington, 2011]

Move to Ready

Proposed resolution:

The suggested wording changes are against the FDIS.

1. Change 20.14.15.4 [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 20.14.15.4 [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 20.14.15.4 [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 32.9.9 [futures.async] is intended to provide rvalues as arguments of `INVOKE`.

   Change the function signatures in header `<future>` synopsis 32.9.1 [futures.overview] p. 1 and in 32.9.9 [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 32.9.9 [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`.