Issue 2021: Further incorrect usages of result_of (original) (raw)
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: 22.10.15.4 [func.bind.bind], 32.10.1 [futures.overview], 32.10.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(i) 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:
- According to 22.10.15.4 [func.bind.bind] p. 3:
[..] The effect of
g(u1, u2, ..., uM)shall beINVOKE(fd, v1, v2, ..., vN, result_of<FD cv (V1, V2, ..., VN)>::type)[..]
butfdis defined as "an lvalue of typeFDconstructed fromstd::forward<F>(f)". This means that the above usage must refer toresult_of<FD cv **&** (V1, V2, ..., VN)>instead. - Similar in 22.10.15.4 [func.bind.bind] p. 10 bullet 2 we have:
if the value of
is_bind_expression<TiD>::valueis true, the argument istid(std::forward<Uj>(uj)...)and its typeViisresult_of<TiD cv (Uj...)>::type
Again,tidis defined as "lvalue of typeTiDconstructed fromstd::forward<Ti>(ti)". This means that the above usage must refer toresult_of<TiD cv **&** (Uj...)>instead. We also have similar defect as in2017(i) in regard to the argument types, this leads us to the further corrected formresult_of<TiD cv **&** (Uj**&&**...)>. This is not the end: Since theViare similar sensitive to the argument problem, the last part must say:
"[..] its typeViisresult_of<TiD cv **&** (Uj**&&**...)>::type **&&**"
(The bound argumentsVican never bevoidtypes, therefore we don't need to use the more defensivestd::add_rvalue_referencetype trait) - The function template
asyncis 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 inreference_wrapper(2017(i)) and more: According to the specification in 32.10.9 [futures.async] the effective result type is that of the call of
INVOKE(decay_copy(std::forward(f)), decay_copy(std::forward(args))...)
First,decay_copypotentially modifies the effective types todecay<F>::typeanddecay<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.10.9 [futures.async] p. 3:
Invocation of the deferred function evaluatesINVOKE(g, xyz)wheregis the stored value ofdecay_copy(std::forward<F>(f))andxyzis the stored copy ofdecay_copy(std::forward<Args>(args))....
This seems to imply that lvalues are provided in contrast to the direct call expression of 32.10.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_wrapperthat 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.
- Change 22.10.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>::valueshall be true. For eachTiinBoundArgs,is_constructible<TiD, Ti>::valueshall be true._INVOKE_(fd, w1, w2, ..., wN)(20.8.2) shall be a valid expression for some valuesw1,w2, ...,wN, whereN == sizeof...(bound_args).-3- Returns: A forwarding call wrapper
gwith a weak result type (20.8.2). The effect ofg(u1, u2, ..., uM)shall beINVOKE(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 ofgand the values and types of the bound argumentsv1,v2, ...,vNare determined as specified below. […] - Change 22.10.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>::valueshall be true. For eachTiinBoundArgs,is_constructible<TiD, Ti>::valueshall be true._INVOKE_(fd, w1, w2, ..., wN)shall be a valid expression for some valuesw1,w2, ...,wN, whereN == sizeof...(bound_args).-7- Returns: A forwarding call wrapper
gwith a nested typeresult_typedefined as a synonym forR. The effect ofg(u1, u2, ..., uM)shall beINVOKE(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN), R), where the values and types of the bound argumentsv1,v2, ...,vNare determined as specified below. […] - Change 22.10.15.4 [func.bind.bind] p. 10 as indicated:
-10- The values of the bound arguments
v1,v2, ...,vNand their corresponding typesV1,V2, ...,VNdepend on the typesTiDderived from the call to bind and the _cv_-qualifiers cv of the call wrappergas follows:- if
TiDisreference_wrapper<T>, the argument istid.get()and its typeViisT&; - if the value of
is_bind_expression<TiD>::valueistrue, the argument istid(std::forward<Uj>(uj)...)and its typeViisresult_of<TiD _cv_ & (Uj&&...)>::type&&; - if the value
jofis_placeholder<TiD>::valueis not zero, the argument isstd::forward<Uj>(uj)and its typeViisUj&&; - otherwise, the value is
tidand its typeViisTiD _cv_ &.
- if
- This resolution assumes that the wording of 32.10.9 [futures.async] is intended to provide rvalues as arguments of
INVOKE.
Change the function signatures in header<future>synopsis 32.10.1 [futures.overview] p. 1 and in 32.10.9 [futures.async] p. 1 as indicated:template <class F, class... Args>
future<typename result_of<typename decay::type(typename decay::type...)>::type>
async(F&& f, Args&&... args);
template <class F, class... Args>
future<typename result_of<typename decay::type(typename decay::type...)>::type>
async(launch policy, F&& f, Args&&... args); - Change 32.10.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::deferredis non-zero — Stores_DECAYCOPY_(std::forward<F>(f))and_DECAYCOPY_(std::forward<Args>(args))...in the shared state. These copies offandargsconstitute a deferred function. Invocation of the deferred function evaluates_INVOKE_(std::move(g), std::move(xyz))wheregis the stored value of_DECAYCOPY_(std::forward<F>(f))andxyzis the stored copy of_DECAYCOPY_(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 apolicyvalue oflaunch::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 typefuture<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>that refers to the associated asynchronous state created by this call toasync.