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

### 815. `std::function` and `reference_closure` do not use perfect forwarding

Section: 20.14.17.3.5 [func.wrap.func.inv] Status: Resolved Submitter: Alisdair Meredith Opened: 2008-03-16 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [func.wrap.func.inv].

View all issues with Resolved status.

Discussion:

`std::function` and `reference_closure` should use "perfect forwarding" as described in the rvalue core proposal.

[ Sophia Antipolis: ]

> According to Doug Gregor, as far as `std::function` is concerned, perfect forwarding can not be obtained because of type erasure. Not everyone agreed with this diagnosis of forwarding.

[ 2009-05-01 Howard adds: ]

> Sebastian Gesemann brought to my attention that the `CopyConstructible` requirement on `function`'s `ArgTypes...` is an unnecessary restriction.

> >     template<Returnable R, CopyConstructible... ArgTypes>
> >     class function<R(ArgTypes...)>
> >     ...

> On further investigation, this complaint seemed to be the same issue as this one. I believe the reason `CopyConstructible` was put on `ArgTypes` in the first place was because of the nature of the invoke member:

> >     template<class R, class ...ArgTypes>
> >     R
> >     function<R(ArgTypes...)>::operator()(ArgTypes... arg) const
> >     {
> >         if (f_ == 0)
> >             throw bad_function_call();
> >         return (*f_)(arg...);
> >     }

> However now with rvalue-refs, "by value" no longer implies `CopyConstructible` (as Sebastian correctly points out). If rvalue arguments are supplied, `MoveConstructible` is sufficient. Furthermore, the constraint need not be applied in `function` if I understand correctly. Rather the client must apply the proper constraints at the call site. Therefore, at the very least, I recommend that `CopyConstructible` be removed from the template class `function`.

> Furthermore we need to mandate that the invoker is coded as:

> >     template<class R, class ...ArgTypes>
> >     R
> >     function<R(ArgTypes...)>::operator()(ArgTypes... arg) const
> >     {
> >         if (f_ == 0)
> >             throw bad_function_call();
> >         return (*f_)(std::forward<ArgTypes>(arg)...);
> >     }

> Note that `ArgTypes&&` (the "perfect forwarding signature") is not appropriate here as this is not a deduced context for `ArgTypes`. Instead the client's arguments must implicitly convert to the non-deduced `ArgType` type. Catching these arguments by value makes sense to enable decay.

> Next `forward` is used to move the `ArgTypes` as efficiently as possible, and also with minimum requirements (not `CopyConstructible`) to the type-erased functor. For object types, this will be a `move`. For reference type `ArgTypes`, this will be a copy. The end result must be that the following is a valid program:

> >     #include <functional>
> >     #include <memory>
> >     #include <cassert>

> >     std::unique_ptr<int>
> >     f(std::unique_ptr<int> p, int& i)
> >     {
> >         ++i;
> >         return std::move(p);
> >     }

> >     int main()
> >     {
> >         int i = 2;
> >         std::function<std::unique_ptr<int>(std::unique_ptr<int>,
> >                                            int&> g(f);
> >         std::unique_ptr<int> p = g(std::unique_ptr<int>(new int(1)), i);
> >         assert(*p == 1);
> >         assert(i == 3);
> >     }

> [ Tested in pre-concepts rvalue-ref-enabled compiler. ]

> In the example above, the first `ArgType` is `unique_ptr<int>` and the second `ArgType` is `int&`. Both must work!

[ 2009-05-27 Daniel adds: ]

> in the 2009-05-01 comment of above mentioned issue Howard

> 1. Recommends to replace the `CopyConstructible` requirement by a `MoveConstructible` requirement
> 2. Says: "Furthermore, the constraint need not be applied in `function` if I understand correctly. Rather the client must apply the proper constraints at the call site"

> I'm fine with (a), but I think comment (b) is incorrect, at least in the sense I read these sentences. Let's look at Howard's example code:

> >     function<R(ArgTypes...)>::operator()(ArgTypes... arg) const
> >     {
> >        if (f_ == 0)
> >            throw bad_function_call();
> >        return (*f_)(std::forward<ArgTypes>(arg)...);
> >     }

> In the constrained scope of this `operator()` overload the expression "`(*f_)(std::forward<ArgTypes>(arg)...)`" must be valid. How can it do so, if `ArgTypes` aren't at least `MoveConstructible`?

[ 2009-07 Frankfurt: ]

> Leave this open and wait until concepts are removed from the Working Draft so that we know how to write the proposed resolution in terms of diffs to otherwise stable text.

[ 2009-10 Santa Cruz: ]

> Leave as open. Howard to provide wording. Howard welcomes any help.

[ 2009-12-12 Jonathan Wakely adds: ]

> 20.14.17.3 [func.wrap.func] says

> > 2 A function object `f` of type `F` is Callable for argument types `T1, T2, ..., TN` in `ArgTypes` and a return type `R`, if, given lvalues `t1, t2, ..., tN` of types `T1, T2, ..., TN`, respectively, `INVOKE (f, t1, t2, ..., tN)` is well formed (20.7.2) and, if `R` is not `void`, convertible to `R`.

> N.B. lvalues, which means you can't use `function<R(T&&)>` or `function<R(unique_ptr<T>)>`

> I recently implemented rvalue arguments in GCC's `std::function`, all that was needed was to use `std::forward<ArgTypes>` in a few places. The example in issue 815 works.

> I think 815 could be resolved by removing the requirement that the target function be callable with lvalues. Saying `ArgTypes` need to be `CopyConstructible` is wrong, and IMHO saying `MoveConstructible` is unnecessary, since the by-value signature implies that already, but if it is needed it should only be on `operator()`, not the whole class (you could in theory instantiate `std::function<R(noncopyable)>` as long as you don't invoke the call operator.)

> I think defining invocation in terms of `INVOKE` already implies perfect forwarding, so we don't need to say explicitly that `std::forward` should be used (N.B. the types that are forwarded are those in `ArgTypes`, which can differ from the actual parameter types of the target function. The actual parameter types have gone via type erasure, but that's not a problem - IMHO forwarding the arguments as `ArgTypes` is the right thing to do anyway.)

> Is it sufficient to simply replace "lvalues" with "values"? or do we need to say something like "lvalues when `Ti` is an lvalue-reference and rvalues otherwise"? I prefer the former, so I propose the following resolution for 815:

> Edit 20.14.17.3 [func.wrap.func] paragraph 2:

> > 2 A function object `f` of type `F` is Callable for argument types `T1, T2, ..., TN` in `ArgTypes` and a return type `R`, if, given lvalues `t1, t2, ..., tN` of types `T1, T2, ..., TN`, respectively, `INVOKE (f, t1, t2, ..., tN)` is well formed (20.7.2) and, if `R` is not `void`, convertible to `R`.

[ 2009-12-12 Daniel adds: ]

> I don't like the reduction to "values" and prefer the alternative solution suggested using "lvalues when Ti is an lvalue-reference and rvalues otherwise". The reason why I dislike the shorter version is based on different usages of "values" as part of defining the semantics of requirement tables via expressions. E.g. 16.4.4.2 [utility.arg.requirements]/1 says "`a`, `b`, and `c` are values of type `const T;`" or similar in 22.2.1 [container.requirements.general]/4 or /14 etc. My current reading of all these parts is that both rvalues and lvalues are required to be supported, but this interpretation would violate the intention of the suggested fix of #815, if I correctly understand Jonathan's rationale.

[ 2009-12-12 Howard adds: ]

> > "lvalues when Ti is an lvalue-reference and rvalues otherwise"

> doesn't quite work here because the `Ti` aren't deduced. They are specified by the `function` type. `Ti` might be `const int&` (an lvalue reference) and a valid `ti` might be `2` (a non-const rvalue). I've taken another stab at the wording using "expressions" and "bindable to".

[ 2010-02-09 Wording updated by Jonathan, Ganesh and Daniel. ]

[ 2010-02-09 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]

[ 2010-02-10 Daniel opens to improve wording. ]

[ 2010-02-11 This issue is now addressed by 870. ]

[ 2010-02-12 Moved to Tentatively NAD Editorial after 5 positive votes on c++std-lib. Rationale added below. ]

Rationale:

Addressed by 870.

Proposed resolution:

Edit 20.14.17.3 [func.wrap.func] paragraph 2:

> 2 A function object `f` of type `F` is Callable for argument types `T1, T2, ..., TN` in `ArgTypes` and a return type `R`, if, given lvalues `t1, t2, ..., tN` of types `T1, T2, ..., TN`, respectively, the expression `INVOKE(f, declval<ArgTypes>()..., Rt1, t2, ..., tN)`, considered as an unevaluated operand (7 [expr]), is well formed (20.7.2) and, if `R` is not `void`, convertible to `R`.