---
title: "P2721R1"
document: P2721R1
date: 2026-06-17
audience: LEWG
reply-to:
  - "Michael Florian Hava <mfh.cpp@gmail.com>"
---

**Poll:** Forward “P2721R0: Deprecating function” to LWG for C++26 (to be confirmed by an electronic poll).

## SF

## 6

## Outcome: No consensus for change

### [functional.syn]

### [func.wrap.general]

### [depr.func]

**??.??.? Header** `<functional>` **synopsis** **[functional.syn]**

:::wording-remove

… namespace std { … // [func.wrap], polymorphic function wrappers <del>// [func.wrap.badcall], class bad_function_call</del> <del>class bad_function_call;</del>

:::

:::wording-remove

<del>// [func.wrap.func], class template function</del> <del>template<class> class function; // not defined</del> <del>template<class R, class... ArgTypes> class function<R(ArgTypes...)>;</del>

:::

:::wording-remove

<del>// [func.wrap.func.alg], function specialized algorithms</del> <del>template<class R, class... ArgTypes></del> <del>void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;</del>

:::

:::wording-remove

<del>// [func.wrap.func.nullptr], function null pointer comparison operator functions</del> <del>template<class R, class... ArgTypes></del> <del>bool operator==(const function<R(ArgTypes...)>&, nullptr_t) noexcept;</del>

:::

*//* ***[func.wrap.move],*** *move-only wrapper* `…` `}`

…

**??.??.??.? General** **[func.wrap.general]**

1 Subclause **[func.wrap]** describes polymorphic wrapper classes that encapsulate arbitrary callable objects.

:::wording-remove

2 Let t be an object of a type that is a specialization of <del>function,</del> copyable_function, move_only_function, or function_ref, such that the target object x of t has a type that is a specialization of <del>function,</del> copyable_function, move_only_function, or function_ref. Each argument of the invocation of x evaluated as part of the invocation of t may alias an argument in the same position in the invocation of t that has the same type, even if the corresponding parameter is not of reference type. [Example 1: move_only_function<void(T)> f{copyable_function<void(T)>{[](T) {}}}; T t; f(t); // it is unspecified how many copies of T are made — end example]

:::

3 *Recommended* practice: Implementations should avoid double wrapping when constructing polymorphic wrappers from one another.

| D.?? Deprecated `function` | [depr.func] |
| --- | --- |
| **D.??.1 Header <functional> synopsis** | **[depr.func.syn]** |

:::wording-add

<ins>The header <functional> has the following additions:</ins>

:::

:::wording-add

<ins>namespace std {</ins> <ins>// [depr.func.badcall], class bad_function_call</ins> <ins>class bad_function_call;</ins>

:::

:::wording-add

<ins>// [depr.func.wrap], class template function</ins> <ins>template<class> class function; // not defined</ins> <ins>template<class R, class... ArgTypes> class function<R(ArgTypes...)>;</ins>

:::

:::wording-add

<ins>// [depr.func.alg], function specialized algorithms</ins> <ins>template<class R, class... ArgTypes></ins> <ins>void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;</ins>

:::

:::wording-add

<ins>// [depr.func.nullptr], function null pointer comparison operator functions</ins> <ins>template<class R, class... ArgTypes></ins> <ins>bool operator==(const function<R(ArgTypes...)>&, nullptr_t) noexcept;</ins> <ins>}</ins>

:::

:::wording-add

<ins>D.??.2 General</ins> <ins>[depr.func.general]</ins>

:::

1 In **[func.wrap.general]/2** both `t` and `x` can also be of a type that is a specialization of `function`. **[DRAFTING NOTE: The intention here is to replicate the behavior from before the deprecation.]**

#### [func.wrap.badcall] and [func.wrap.func]

:::wording

<del>??.??.??.?</del><ins>D.??.3</ins> Class bad_function_call <del>[func.wrap.badcall]</del><ins>[depr.func.badcall]</ins>

:::

:::wording

1 An exception of type bad_function_call is thrown by function::operator() <del>([func.wrap.func.inv]</del><ins>[depr.func.wrap.inv])</ins> when the function wrapper object has no target.

:::

```cpp
    namespace std { 
    class bad_function_call : public exception { 
    public: 
        // see [exception] for the specification of the special member functions 
    const char* what() const noexcept override; 
    };
const char* what() const noexcept override;
```

2 *Returns:* An implementation-defined `NTBS`.

| ??.??.??.?D.??.4 Class template `function` | [func.wrap.func][depr.func.wrap] |
| --- | --- |
| **??.??.??.?.1****D.??.4.1** **General** | **[func.wrap.func.general]****[depr.func.wrap.general]** |

```cpp
  template<class R, class... ArgTypes> 
  class function<R(ArgTypes...)> { 
  public: 
    using result_type = R; 
```

:::wording

// <del>[func.wrap.func.con]</del><ins>[depr.func.wrap.con],</ins> construct/copy/destroy function() noexcept; function(nullptr_t) noexcept; function(const function&); function(function&&) noexcept; template<class F> function(F&&); function& operator=(const function&); function& operator=(function&&); function& operator=(nullptr_t) noexcept; template<class F> function& operator=(F&&); template<class F> function& operator=(reference_wrapper<F>) noexcept;

:::

```cpp
    ~function(); 
```

:::wording

// <del>[func.wrap.func.mod]</del><ins>[depr.func.wrap.mod],</ins> function modifiers void swap(function&) noexcept;

:::

:::wording

// <del>[func.wrap.func.cap]</del><ins>[depr.func.wrap.cap],</ins> function capacity explicit operator bool() const noexcept;

:::

:::wording

// <del>[func.wrap.func.inv]</del><ins>[depr.func.wrap.inv],</ins> function invocation R operator()(ArgTypes...) const;

:::

:::wording

// <del>[func.wrap.func.targ]</del><ins>[dep.func.wrap.targ],</ins> function target access const type_info& target_type() const noexcept; template<class T> T* target() noexcept; template<class T> const T* target() const noexcept; }; template<class R, class... ArgTypes> function(R(*)(ArgTypes...)) -> function<R(ArgTypes...)>; template<class F> function(F) -> function<see below>; }

:::

1 The `function` class template provides polymorphic wrappers that generalize the notion of a function pointer. Wrappers can store, copy, and call arbitrary callable objects **([func.def]),** given a call signature **([func.def]**).

2 The `function` class template is a call wrapper **([func.def])** whose call signature **([func.def])** is `R(ArgTypes...)`.

3 [Note 1: The types deduced by the deduction guides for `function` might change in future revisions of C++. *— end* note]

:::wording

<del>??.??.??.?.2</del><ins>D.??.4.2</ins> Constructors and destructor <del>[func.wrap.func.con]</del><ins>[depr.func.wrap.con]</ins> function() noexcept;

:::

1 *Postconditions:* `!*this`.

```cpp
function(nullptr_t) noexcept;
```

2 *Postconditions:* `!*this`.

```cpp
function(const function& f);
```

3 *Postconditions:* `!*this` if `!f`; otherwise, the target object of `*this` is a copy of the target object of `f`.

4 *Throws:* Nothing if `f`'s target is a specialization of `reference_wrapper` or a function pointer. Otherwise, may throw `bad_alloc` or any exception thrown by the copy constructor of the stored callable object.

5 *Recommended* practice: Implementations should avoid the use of dynamically allocated memory for small callable objects, for example, where `f`'s target is an object holding only a pointer or reference to an object and a member function pointer.

```cpp
function(function&& f) noexcept;
```

6 *Postconditions:* If `!f`, `*this` has no target; otherwise, the target of `*this` is equivalent to the target of `f` before the construction, and `f` is in a valid state with an unspecified value.

7 *Recommended* practice: Implementations should avoid the use of dynamically allocated memory for small callable objects, for example, where `f`'s target is an object holding only a pointer or reference to an object and a member function pointer.

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

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

9 *Constraints*:

(9.1) — `is_same_v<remove_cvref_t<F>, function>` is `false`, and

(9.2) — `is_invocable_r_v<R, FD&, ArgTypes...>` is `true`.

10 *Mandates*:

(10.1) — `is_copy_constructible_v<FD>` is `true`, and

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

11 *Preconditions:* `FD` meets the *Cpp17CopyConstructible* requirements.

12 *Postconditions:* `!*this` is `true` if any of the following hold:

(12.1) — `f` is a null function pointer value.

(12.2) — `f` is a null member pointer value.

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

13 Otherwise, `*this` has a target object of type `FD` direct-non-list-initialized with `std::forward<F>(f)`.

14 *Throws:* Nothing if `FD` is a specialization of `reference_wrapper` or a function pointer type. Otherwise, may throw `bad_alloc` or any exception thrown by the initialization of the target object.

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

```cpp
template<class F> function(F) -> function<see below>;
```

16 *Constraints:* `&F::operator()` is well-formed when treated as an unevaluated operand and either

(16.1) — `F::operator()` is a non-static member function and `decltype(&F::operator())` is either of the form `R(G::*)(A...)` `cv` `&`opt `noexcept`opt or of the form `R(*)(G, A...) noexcept`opt for a type `G`, or

(16.2) — `F::operator()` is a static member function and `decltype(&F::operator())` is of the form `R(*)(A...) noexcept`opt.

17 *Remarks:* The deduced type is `function<R(A...)>`.

```cpp
18
           [Example 1: 
           void f() { 
           int i{5}; 
           function g = [&](double) { return i; };       // deduces function<int(double)> 
           } 
           — end example]
    function& operator=(const function& f);
```

19 *Effects:* As if by `function(f).swap(*this);`

20 *Returns:* `*this`.

```cpp
function& operator=(function&& f);
```

21 *Effects:* Replaces the target of `*this` with the target of `f`.

22 *Returns:* `*this`.

```cpp
function& operator=(nullptr_t) noexcept;
```

23 *Effects:* If `*this != nullptr`, destroys the target of `this`.

24 *Postconditions:* `!(*this)`.

25 *Returns:* `*this`.

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

26 *Constraints:* `is_invocable_r_v<R, decay_t<F>&, ArgTypes...>` is `true`.

## Acknowledgements

27 *Effects:* As if by: `function(std::forward<F>(f)).swap(*this);`

28 *Returns:* `*this`.

```cpp
template<class F> function& operator=(reference_wrapper<F> f) noexcept;
```

29 *Effects:* As if by: `function(f).swap(*this);`

30 *Returns:* `*this`.

```cpp
~function();
```

31 *Effects:* If `*this != nullptr`, destroys the target of `this`.

:::wording

<del>??.??.??.?.3</del><ins>D.??.4.3</ins> Modifiers <del>[func.wrap.func.mod]</del><ins>[depr.func.wrap.mod]</ins> void swap(function& other) noexcept;

:::

1 *Effects:* Interchanges the target objects of `*this` and `other`.

:::wording

<del>??.??.??.?.4</del><ins>D.??.4.4</ins> Capacity <del>[func.wrap.func.cap]</del><ins>[depr.func.wrap.cap]</ins> explicit operator bool() const noexcept;

:::

1 *Returns:* `true` if `*this` has a target, otherwise `false`.

:::wording

<del>??.??.??.?.5</del><ins>D.??.4.5</ins> Invocation <del>[func.wrap.func.inv]</del><ins>[depr.func.wrap.inv]</ins>

:::

```cpp
R operator()(ArgTypes... args) const;
```

1 *Returns:* `INVOKE<R>(f, std::forward<ArgTypes>(args)...)` **([func.require]),** where `f` is the target object **([func.def])** of `*this`.

2 *Throws:* `bad_function_call` if `!*this;` otherwise, any exception thrown by the target object.

:::wording

<del>??.??.??.?.6</del><ins>D.??.4.6</ins> Target access <del>[func.wrap.func.targ]</del><ins>[depr.func.wrap.targ]</ins> const type_info& target_type() const noexcept;

:::

1 *Returns:* If `*this` has a target of type `T`, `typeid(T)`; otherwise, `typeid(void)`.

```cpp
template<class T>       T* target() noexcept; 
template<class T> const T* target() const noexcept;
```

2 *Returns:* If `target_type() == typeid(T)` a pointer to the stored function target; otherwise a null pointer.

:::wording

<del>??.??.??.?.7</del><ins>D.??.4.7</ins> Null pointer comparison operator functions <del>[func.wrap.func.nullptr]</del><ins>[depr.func.wrap.nullptr]</ins> template<class R, class... ArgTypes> bool operator==(const function<R(ArgTypes...)>& f, nullptr_t) noexcept;

:::

1 *Returns:* `!f`.

:::wording

<del>??.??.??.?.8</del><ins>D.??.4.8</ins> Specialized algorithms <del>[func.wrap.func.alg]</del><ins>[depr.func.wrap.alg]</ins> template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>& f1, function<R(ArgTypes...)>& f2) noexcept;

:::

1 *Effects:* As if by: `f1.swap(f2);`

<!-- tomd:glyph-placeholders: placeholders=0 skipped_coincident=2 skipped_code_section=0 -->
