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

### 3493. The constructor of `std::function` taking an `F` is missing a constraint

Section: 22.10.17.3.2 [func.wrap.func.con] Status: New Submitter: Ville Voutilainen Opened: 2020-10-31 Last modified: 2021-08-20

Priority: 3

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

View all issues with New status.

Discussion:

In P0288, `any_invocable` is (correctly) constraining its constructor that takes an `F`:

>     template<class F> any_invocable(F&& f);

> > Let `VT` be `decay_t<F>`.

> > Constraints:

> > 1. — […]

> > 2. — `is_constructible_v<VT, F>` is `true`, and

> > 3. — […]

`std::function` doesn't do that. According to N4868, 22.10.17.3.2 [func.wrap.func.con] p8 has a constraint for Lvalue-Callable, but not for copy-constructibility. There is a precondition in p9, but that's not enough for portable well/ill-formedness.

Since this is a constructor, and we want to give the right answer to `is_constructible`/`constructible_from` queries, we should add the relevant constraint.

[2020-11-01; Daniel comments]

This issue has some overlap with LWG 2774.

[2021-01-15; Telecon prioritization]

Set priority to 3 following reflector and telecon discussions.

[2021-05-17; Tim comments]

The new constraint causes constraint recursion in an example like:

>     struct C {
>         explicit C(std::function<void()>); // #1
>         void operator()() {}
>     };
>     static_assert(std::is_constructible_v<C, const C&>);

Here, to determine whether a `C` can be constructed from a `const C` lvalue, the overload resolution will attempt to determine whether the constructor marked `#1` is a viable candidate, which involves a determination of whether that lvalue can be implicitly converted to a `std::function<void()>`, which, with the new constraint, requires a determination whether `C` is copy-constructible — in other words, whether it can be constructed from a `C` lvalue.

This is similar to LWG 3420: in both cases we have a class (`filesystem::path` there, `function` here) that is convertible from every type that are, inter alia, copy constructible, and this then results in constraint recursion when we ask whether a different type that is constructible from such a class is copy constructible.

The `C` above is reduced from an internal helper type in libstdc++. Given the ubiquity of call wrappers — types that are callable in their own right and therefore may not be able to be ruled out by the Lvalue-Callable constraint, and can also naturally have a constructor that take the wrapped function object as the argument, triggering the recursion scenario — it is not clear that there is a good way to add this constraint without causing undue breakage.

[2021-08-20; LWG telecon]

LWG requested that the constraint cited above for `move_only_function` (né `any_invocable`) be moved to a Mandates: element instead, to avoid the same constraint recursion.

Proposed resolution:

This wording is relative to N4868.

1. Modify 22.10.17.3.2 [func.wrap.func.con] as indicated:

   >     template<class F> function(F f);

   > > -8- Constraints: `F` is Lvalue-Callable (22.10.17.3.1 [func.wrap.func.general]) for argument types `ArgTypes...` and return type `R`, and `is_copy_constructible_v<F>` is `true`.

   > > -9- Preconditions: `F` meets the Cpp17CopyConstructible requirements.

   > > […]