<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2370: Operations involving type-erased allocators should not be noexcept in std::function</title>
<meta property="og:title" content="Issue 2370: Operations involving type-erased allocators should not be noexcept in std::function">
<meta property="og:description" content="C++ library issue. Status: Resolved">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2370.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#Resolved">Resolved</a> status.</em></p>
<h3 id="2370"><a href="lwg-defects.html#2370">2370</a>. Operations involving type-erased allocators should not be <code>noexcept</code> in <code>std::function</code></h3>
<p><b>Section:</b> 22.10.17.3 <a href="https://wg21.link/func.wrap.func">[func.wrap.func]</a> <b>Status:</b> <a href="lwg-active.html#Resolved">Resolved</a>
 <b>Submitter:</b> Pablo Halpern <b>Opened:</b> 2014-02-27 <b>Last modified:</b> 2020-09-06</p>
<p><b>Priority: </b>3
</p>
<p><b>View all other</b> <a href="lwg-index.html#func.wrap.func">issues</a> in [func.wrap.func].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Resolved">Resolved</a> status.</p>
<p><b>Discussion:</b></p>
<p>
The following constructors in 22.10.17.3 <a href="https://wg21.link/func.wrap.func">[func.wrap.func]</a> are declared <code>noexcept</code>, even
though it is not possible for an implementation to guarantee that they will not throw:
</p>

<blockquote><pre>
template &lt;class A&gt; function(allocator_arg_t, const A&amp;) noexcept;
template &lt;class A&gt; function(allocator_arg_t, const A&amp;, nullptr_t) noexcept;
</pre></blockquote>

<p>
In addition, the following functions are guaranteed not to throw if the target
is a function pointer or a <code>reference_wrapper</code>:
</p>

<blockquote><pre>
template &lt;class A&gt; function(allocator_arg_t, const A&amp; a, const function&amp; f);
template &lt;class F, class A&gt; function(allocator_arg_t, const A&amp; a, F f);
</pre></blockquote>

<p>
In all of the above cases, the function object might need to allocate memory
(an operation that can throw) in order to hold a copy of the type-erased
allocator itself. The first two constructors produce an empty function
object, but the allocator is still needed in case the object is later assigned
to. In this case, we note that the propagation of allocators on assignment is
underspecified for <code>std::function</code>. There are three possibilities:
</p>

<ol>
<li><p>The allocator is never copied on copy-assignment, moved on move-assignment, or swapped on swap.</p>
</li>

<li><p>The allocator is always copied on copy-assignment, moved on move-assignment, and swapped on swap.</p>
</li>

<li><p>Whether or not the allocator is copied, moved, or swapped is determined at
   run-time based on the <code>propagate_on_container_copy_assignment</code> and
   <code>propagate_on_container_move_assignment</code> traits of the allocators at
   construction of the source function, the target function, or both.</p>
</li>
</ol>

<p>
Although the third option seems to be the most consistent with existing
wording in the containers section of the standard, it is problematic in a
number of respects. To begin with, the propagation behavior is determined at
run time based on a pair of type-erased allocators, instead of at compile
time. Such run-time logic is <em>not</em> consistent with the rest of the standard
and is hard to reason about. Additionally, there are two allocator types
involved, rather than one. Any set of rules that attempts to rationally
interpret the propagation traits of both allocators is likely to be arcane
at best, and subtly wrong for some set of codes at worst.
</p>

<p>
The second option is a non-starter. Historically, and in the vast majority of
existing code, an allocator does not change after an object is constructed.
The second option, if adopted, would undermine the programmer's ability to
construct, e.g., an array of function objects, all using the same allocator.
</p>

<p>
The first option is (in Pablo's opinion) the simplest and best. It is
consistent with historical use of allocators, is easy to understand, and
requires minimal wording. It is also consistent with the wording in N3916,
which formalizes type-erased allocators.
</p>

<p>
For cross-referencing purposes: The resolution of this issue should be
harmonized with any resolution to LWG <a href="lwg-defects.html#2062" title="Effect contradictions w&#47;o no-throw guarantee of std::function swaps (Status: C++17)">2062</a><sup><a href="https://cplusplus.github.io/LWG/issue2062" title="Latest snapshot">(i)</a></sup>, which questions the <code>noexcept</code>
specification on the following member functions of std::function:
</p>

<blockquote><pre>
template &lt;class F&gt; function&amp; operator=(reference_wrapper&lt;F&gt;) noexcept;
void swap(function&amp;) noexcept;
</pre></blockquote>

<p><i>[2015-05 Lenexa]</i></p>

<p>
MC: change to P3 and status to open.
<p/>
STL: note that <code>noexcept</code> is an issue and large chunks of allocator should be destroyed.
</p>

<p><i>[2015-12-16, Daniel comments]</i></p>

<p>
See <a href="lwg-defects.html#2564" title="[fund.ts.v2] std::experimental::function constructors taking allocator arguments may throw exceptions (Status: Resolved)">2564</a><sup><a href="https://cplusplus.github.io/LWG/issue2564" title="Latest snapshot">(i)</a></sup> for a corresponding issue addressing library fundamentals v2.
</p>

<p><strong>Previous resolution [SUPERSEDED]:</strong></p>
<blockquote class="note">
<p>This wording is relative to N3936.</p>

<ol>
<li><p>Change 22.10.17.3 <a href="https://wg21.link/func.wrap.func">[func.wrap.func]</a>, class template <code>function</code> synopsis, as indicated:</p>

<blockquote><pre>
template &lt;class A&gt; function(allocator_arg_t, const A&amp;) <del>noexcept</del>;
template &lt;class A&gt; function(allocator_arg_t, const A&amp;, nullptr_t) <del>noexcept</del>;
</pre></blockquote>
</li>

<li><p>Change 22.10.17.3.2 <a href="https://wg21.link/func.wrap.func.con">[func.wrap.func.con]</a> as indicated:</p>

<blockquote>
<p>
-1- When any function constructor that takes a first argument of type <code>allocator_arg_t</code> is invoked, the second
argument shall have a type that conforms to the requirements for <code>Allocator</code> (Table 17.6.3.5). A copy of the
allocator argument is used to allocate memory, if necessary, for the internal data structures of the constructed
function object. <ins> For the remaining constructors, an instance of <code>allocator&lt;T&gt;</code>, for some suitable type
<code>T</code>, is used to allocate memory, if necessary, for the internal data structures of the constructed function object.</ins>
</p>

<pre>
function() noexcept;
template &lt;class A&gt; function(allocator_arg_t, const A&amp;) <del>noexcept</del>;
</pre>

<blockquote>
<p>
-2- <i>Postconditions</i>: <code>!*this</code>.
</p>
</blockquote>

<pre>
function(nullptr_t) noexcept;
template &lt;class A&gt; function(allocator_arg_t, const A&amp;, nullptr_t) <del>noexcept</del>;
</pre>

<blockquote><p>
-3- <i>Postconditions</i>: <code>!*this</code>.
</p></blockquote>

<pre>
function(const function&amp; f);
<del>template &lt;class A&gt; function(allocator_arg_t, const A&amp; a, const function&amp; f);</del>
</pre>

<blockquote><p>
-4- <i>Postconditions</i>: <code>!*this</code> if <code>!f</code>; otherwise, <code>*this</code> targets a copy of <code>f.target()</code>.
</p>

<p>
-5- <i>Throws</i>: shall not throw exceptions if <code>f</code>'s target is a callable object passed
via <code>reference_wrapper</code> or a function pointer. Otherwise, may throw <code>bad_alloc</code>
or any exception thrown by the copy constructor of the stored callable
object. [<i>Note</i>: Implementations are encouraged to avoid the use of
dynamically allocated memory for small callable objects, for example, where
<code>f</code>'s target is an object holding only a pointer or reference to an object and
a member function pointer. &mdash; <i>end note</i>]
</p></blockquote>

<pre>
<ins>template &lt;class A&gt; function(allocator_arg_t, const A&amp; a, const function&amp; f);</ins>
</pre>

<blockquote><p>
<ins>-?- <i>Postconditions</i>: <code>!*this</code> if <code>!f</code>; otherwise, <code>*this</code> targets a copy of <code>f.target()</code>.</ins>
</p></blockquote>

<pre>
function(function&amp;&amp; f);
template &lt;class A&gt; function(allocator_arg_t, const A&amp; a, function&amp;&amp; f);
</pre>

<blockquote><p>
-6- <i>Effects</i>: If <code>!f</code>, <code>*this</code> has no target; otherwise, move-constructs the target
of <code>f</code> into the target of <code>*this</code>, leaving <code>f</code> in a valid state with an
unspecified value. <ins>If an allocator is not specified, the constructed function will use the same allocator as <code>f</code>.</ins>
</p></blockquote>

<pre>
template&lt;class F&gt; function(F f);
template &lt;class F, class A> function(allocator_arg_t, const A&amp; a, F f);
</pre>

<blockquote>
<p>
-7- <i>Requires</i>: <code>F</code> shall be <code>CopyConstructible</code>. 
<p/>
-8- <i>Remarks</i>: These constructors shall not participate in overload resolution unless <code>f</code> is Callable (20.9.11.2)
for argument types <code>ArgTypes...</code> and return type <code>R</code>.
</p>

<p>
-9- <i>Postconditions</i>: <code>!*this</code> if any of the following hold:
</p>
<ul>
<li><p><code>f</code> is a null function pointer value.</p></li>
<li><p><code>f</code> is a null member pointer value.</p></li>
<li><p><code>F</code> is an instance of the function class template, and <code>!f</code></p></li>
</ul>

<p>
-10- Otherwise, <code>*this</code> targets a copy of <code>f</code> initialized with <code>std::move(f)</code>.
[<i>Note</i>: Implementations are encouraged to avoid the use of dynamically
allocated memory for small callable objects, for example, where <code>f</code>'s target
is an object holding only a pointer or reference to an object and a member
function pointer. &mdash; <i>end note</i>]
</p>

<p>
-11- <i>Throws</i>: shall not throw exceptions when <ins>an allocator is not specified
and</ins> <code>f</code> is a function pointer or a <code>reference_wrapper&lt;T&gt;</code> for some
<code>T</code>. Otherwise, may throw <code>bad_alloc</code> or any exception thrown by <code>F</code>'s copy or
move constructor<ins> or by <code>A</code>'s allocate function</ins>.
</p>
</blockquote>

</blockquote>
</li>

</ol>
</blockquote>

<p><i>[2016-08 Chicago]</i></p>

<p>Tues PM: Resolved by <a href="https://wg21.link/p0302r1">P0302R1</a></p>


<p id="res-2370"><b>Proposed resolution:</b></p>
<p>
Resolved by acceptance of <a href="https://wg21.link/p0302r1">P0302R1</a>.
</p>





</body>
</html>
