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

### 2774. `std::function` construction vs assignment

Section: 20.14.17.3.2 [func.wrap.func.con] Status: WP Submitter: Barry Revzin Opened: 2016-09-14 Last modified: 2021-06-07

Priority: 3

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

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

View all issues with WP status.

Discussion:

I think there's a minor defect in the `std::function` interface. The constructor template is:

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

while the assignment operator template is

>     template <class F> function& operator=(F&& f);

The latter came about as a result of LWG 1288, but that one was dealing with a specific issue that wouldn't have affected the constructor. I think the constructor should also take `f` by forwarding reference, this saves a move in the lvalue/xvalue cases and is also just generally more consistent. Should just make sure that it's stored as `std::decay_t<F>` instead of `F`.

Is there any reason to favor a by-value constructor over a forwarding-reference constructor?

[2019-07-26 Tim provides PR.]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4820.

> 1. Edit 20.14.17.3 [func.wrap.func], class template `function` synopsis, as indicated:

>    >     namespace std {
>    >       template<class> class function; // not defined
>    >       template<class R, class... ArgTypes> {
>    >       public:
>    >         using result_type = R;

>    >         // 20.14.17.3.2 [func.wrap.func.con], construct/copy/destroy
>    >         function() noexcept;
>    >         function(nullptr_t) noexcept;
>    >         function(const function&);
>    >         function(function&&) noexcept;
>    >         template<class F> function(F&&);

>    >         […]
>    >       };

>    >       […]
>    >     }

> 2. Edit 20.14.17.3.2 [func.wrap.func.con] p7-11 as indicated:

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

>    > > -7- Requires: `F` shall be Cpp17CopyConstructible Let `FD` be `decay_t<F>`.

>    > > -8- Remarks: This constructor template shall not participate in overload resolution unless `F` Constraints:

>    > > 1. (8.1) — `is_same_v<FD, function>` is `false`; and

>    > > 2. (8.2) — `FD` is Lvalue-Callable (20.14.17.3 [func.wrap.func]) for argument types `ArgTypes...` and return type `R`.

>    > > -?- Expects: `FD` meets the Cpp17CopyConstructible requirements.

>    > > -9- Ensures: `!*this` if any of the following hold:

>    > > 1. (9.1) — `f` is a null function pointer value.

>    > > 2. (9.2) — `f` is a null member pointer value.

>    > > 3. (9.3) — `F` is an instance `remove_cvref_t<F>` is a specialization of the `function` class template, and `!f` is `true`.

>    > > -10- Otherwise, `*this` targets a copy of `f` an object of type `FD` direct-non-list-initialized with `std::move(f)` `std::forward<F>(f)`. [Note: Implementations should avoid the use of dynamically allocated memory for small callable objects, for example, where `f` is refers to an object holding only a pointer or reference to an object and a member function pointer. — end note]

>    > > -11- Throws: Shall Does not throw exceptions when `f` `FD` is a function pointer type or a specialization of `reference_wrapper``<T>` for some `T`. Otherwise, may throw `bad_alloc` or any exception thrown by `F`’s copy or move constructor the initialization of the target object.

[2020-11-01; Daniel comments and improves the wording]

The proposed wording should — following the line of Marshall's "Mandating" papers — extract from the Cpp17CopyConstructible precondition a corresponding Constraints: element and in addition to that the wording should replace old-style elements such as Expects: by the recently agreed on elements.

See also the related issue LWG 3493.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4868.

> 1. Edit 20.14.17.3 [func.wrap.func], class template `function` synopsis, as indicated:

>    >     namespace std {
>    >       template<class> class function; // not defined

>    >       template<class R, class... ArgTypes> {
>    >       public:
>    >         using result_type = R;

>    >         // 20.14.17.3.2 [func.wrap.func.con], construct/copy/destroy
>    >         function() noexcept;
>    >         function(nullptr_t) noexcept;
>    >         function(const function&);
>    >         function(function&&) noexcept;
>    >         template<class F> function(F&&);

>    >         […]
>    >       };

>    >       […]
>    >     }

> 2. Edit 20.14.17.3.2 [func.wrap.func.con] as indicated:

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

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

>    > > -8- Constraints:

>    > > 1. (8.1) — `is_same_v<remove_cvref_t<F>, function>` is `false`,

>    > > 2. (8.2) — `FD` `F` is Lvalue-Callable (20.14.17.3.1 [func.wrap.func.general]) for argument types `ArgTypes...` and return type `R`,

>    > > 3. (8.3) — `is_copy_constructible_v<FD>` is `true`, and

>    > > 4. (8.4) — `is_constructible_v<FD, F>` is `true`.

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

>    > > -10- Postconditions: `!*this` if any of the following hold:

>    > > 1. (10.1) — `f` is a null function pointer value.

>    > > 2. (10.2) — `f` is a null member pointer value.

>    > > 3. (10.3) — `F` is an instance `remove_cvref_t<F>` is a specialization of the function class template, and `!f` is `true`.

>    > > -11- Otherwise, `*this` targets a copy of `f` an object of type `FD` direct-non-list-initialized with `std::move(f)std::forward<F>(f)`.

>    > > -12- Throws: Nothing if `fFD` is a specialization of `reference_wrapper` or a function pointer type. Otherwise, may throw `bad_alloc` or any exception thrown by `F`'s copy or move constructor the initialization of the target object.

>    > > -13- Recommended practice: Implementations should avoid the use of dynamically allocated memory for small callable objects, for example, where `f` is refers to an object holding only a pointer or reference to an object and a member function pointer.

[2021-05-17; Tim comments and revises the wording]

The additional constraints added in the previous wording can induce constraint recursion, as noted in the discussion of LWG 3493. The wording below changes them to Mandates: instead to allow this issue to make progress independently of that issue.

The proposed resolution below has been implemented and tested on top of libstdc++.

[2021-05-20; Reflector poll]

Set status to Tentatively Ready after five votes in favour during reflector poll.

[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4885.

1. Edit 20.14.17.3.1 [func.wrap.func.general], class template `function` synopsis, as indicated:

   >     namespace std {
   >       template<class> class function; // not defined

   >       template<class R, class... ArgTypes> {
   >       public:
   >         using result_type = R;

   >         // 20.14.17.3.2 [func.wrap.func.con], construct/copy/destroy
   >         function() noexcept;
   >         function(nullptr_t) noexcept;
   >         function(const function&);
   >         function(function&&) noexcept;
   >         template<class F> function(F&&);

   >         […]
   >       };

   >       […]
   >     }

2. Edit 20.14.17.3.2 [func.wrap.func.con] as indicated:

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

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

   > > -8- Constraints:

   > > 1. (8.1) — `is_same_v<remove_cvref_t<F>, function>` is `false`, and

   > > 2. (8.2) — `FD` `F` is Lvalue-Callable (20.14.17.3.1 [func.wrap.func.general]) for argument types `ArgTypes...` and return type `R`.

   > > -?- Mandates:

   > > 1. (?.1) — `is_copy_constructible_v<FD>` is `true`, and

   > > 2. (?.2) — `is_constructible_v<FD, F>` is `true`.

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

   > > -10- Postconditions: `!*this` is `true` if any of the following hold:

   > > 1. (10.1) — `f` is a null function pointer value.

   > > 2. (10.2) — `f` is a null member pointer value.

   > > 3. (10.3) — `F` is an instance `remove_cvref_t<F>` is a specialization of the `function` class template, and `!f` is `true`.

   > > -11- Otherwise, `*this` targets a copy of `f` an object of type `FD` direct-non-list-initialized with `std::move(f)std::forward<F>(f)`.

   > > -12- Throws: Nothing if `fFD` is a specialization of `reference_wrapper` or a function pointer type. Otherwise, may throw `bad_alloc` or any exception thrown by `F`'s copy or move constructor the initialization of the target object.

   > > -13- Recommended practice: Implementations should avoid the use of dynamically allocated memory for small callable objects, for example, where `f` is refers to an object holding only a pointer or reference to an object and a member function pointer.