<html><head>
<title>Deprecating Allocator Support in std::function</title>
  <style type='text/css'>
  p {text-align:justify}
  li {text-align:justify}
  blockquote.note
  {
          background-color:#E0E0E0;
          padding-left: 15px;
          padding-right: 15px;
          padding-top: 1px;
          padding-bottom: 1px;
  }
  p code {color:navy}
  .wording p code {color:inherited}
  ins p code {color:#00A000}
  ins {color:#00A000}
  del {color:#A00000}
  table#boilerplate { border:0 }
  table#boilerplate td { padding-left: 2em }
  table.bordered, table.bordered th, table.bordered td {
    border: 1px solid;
    text-align: center;
  }
  ins.block {color:#00A000; text-decoration: none}
  del.block {color:#A00000; text-decoration: none}
  </style>
</head><body>
<table id="boilerplate">
<tr><td>Document number</td><td>P0302R0</td></tr>
<tr><td>Date</td><td>2016-03-18</td></tr>
<tr><td>Project</td><td>Programming Language C++, Library Evolution Working Group</td></tr>
<tr><td>Reply-to</td><td>Jonathan Wakely &lt;cxx@kayari.org&gt;</td></tr>
</table><hr>
<h1>Deprecating Allocator Support in std::function</h1>
<h2>Abstract</h2>

<p>The class template <code>std::function</code> has several constructors that take an
allocator argument, but the semantics are unclear, and there are technical
issues with storing an allocator in a type-erased context and then recovering
that allocator later for any allocations needed during copy assignment.
Those constructors should be deprecated and eventually removed.</p>

<h2>Discussion</h2>

<p>The GCC standard library has never provided those constructors at all. The
libc++ implementation declares the constructors but ignores the allocator
arguments. The MSVC implementation uses the allocator on construction, but
does not reuse the allocator if the target is replaced by assignment, which
means allocator propagation is not supported.</p>

<p>There have been a number of issues with allocator support in <code>std::function</code>:</p>

<ul>
<li><a href="http://wg21.link/lwg2385">2385</a> "<code>function::assign</code> allocator argument doesn't make sense" -- The resolution was to remove the ability to specify an allocator when replacing the target.  The notes from the discussion in Lenexa indicate support for removing more, as proposed by this paper.</li>
<li><a href="http://wg21.link/lwg2386">2386</a> "<code>function::operator=</code> handles allocators incorrectly" -- Closed as NAD due to implementation concerns.</li>
<li><a href="http://wg21.link/lwg2062">2062</a> "Effect contradictions w/o no-throw guarantee of <code>std::function</code> swaps" -- Still Open, no clear direction.</li>
<li><a href="http://wg21.link/lwg2370">2370</a> "Operations involving type-erased allocators should not be <code>noexcept</code> in <code>std::function</code>" -- Currently still Open, related to 2062.</li>
<li><a href="http://wg21.link/lwg2501">2501</a> "<code>std::function</code> requires POCMA/POCCA" -- Still Open.</li>
<li><a href="http://wg21.link/lwg2502">2502</a> "<code>std::function</code> does not use <code>allocator::construct</code>" -- Still Open.</li>
</ul>


<p>The attempts to fix allocator support in <code>function</code> using polymorphic memory resources (see <code>std::experimental::function</code> in Library Fundamentals TS) are not without their own issues (<a href="http://wg21.link/lwg2527">2527</a>, <a href="http://wg21.link/lwg2564">2564</a>).</p>

<p>It is clear that allocator support in <code>std::function</code> is poorly-specified
and the source of implementation divergence. I propose that we remove the
relevant constructors (after a period of deprecation).</p>

<h2>Technical Specification</h2>

<p>Move the constructors taking allocators from [func.wrap.func] to Annex D.</p>

<p>Edit the class synopsis in [func.wrap.func] to remove constructors taking allocators:</p>

<pre><code>      // 20.12.12.2.1, construct/copy/destroy:
      function() noexcept;
      function(nullptr_t) noexcept;
      function(const function&amp;);
      function(function&amp;&amp;);
      template&lt;class F&gt; function(F);
      <del class="block">
      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;
      template&lt;class A&gt; function(allocator_arg_t, const A&amp;,
      const function&amp;);
      template&lt;class A&gt; function(allocator_arg_t, const A&amp;,
      function&amp;&amp;);
      template&lt;class F, class A&gt; function(allocator_arg_t, const A&amp;, F);
      </del>
</code></pre>

<p>Edit the class synopsis in [func.wrap.func] to remove the <code>uses_allocator</code> partial specialization:</p>

<pre><code>    // 20.12.12.2.7, specialized algorithms:
    template &lt;class R, class... ArgTypes&gt;
      void swap(function&lt;R(ArgTypes...)&gt;&amp;, function&lt;R(ArgTypes...)&gt;&amp;);
    <del class="block">
    template&lt;class R, class... ArgTypes, class Alloc&gt;
      struct uses_allocator&lt;function&lt;R(ArgTypes...)&gt;, Alloc&gt;
        : true_type { };
  </del>}
</code></pre>

<p>Edit [func.wrap.func.con]:</p>

<blockquote><p>1 <del>When any <code>function</code> 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 Allocator (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 <code>function</code> object.</del></p></blockquote>

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

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

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

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

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

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

<p>5 <em>Throws:</em> 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. <em>[Note:</em> 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. <em>&mdash; end note]</em></p></blockquote>

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

<blockquote><p>6 <em>Effects:</em> 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.</p>

<p>7 <em>Throws:</em> shall not throw exceptions [...]</p></blockquote>

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

<blockquote><p>8 <em>Requires:</em> <code>F</code> shall be <code>CopyConstructible</code>.</p>

<p>9 <em>Remarks:</em> <del>These constructors</del><ins>This constructor</ins> shall not participate in overload resolution unless <code>f</code> is Callable (20.12.12.2) for argument types <code>ArgTypes...</code> and return type <code>R</code>.</p></blockquote>

<p>Create a new subclause in Annex D with the following content (the change to the original text taken from [func.wrap.func] is highlighted):</p>

<blockquote><p> <strong>D.8 Allocator support for <code>function</code> [depr.func.alloc]</strong></p>

<p>1 The following constructors and class template partial specialization are in addition to those specified in Clause 20:</p></blockquote>

<pre><code>      namespace std {
        template&lt;class R, class... ArgTypes&gt;
        class function&lt;R(ArgTypes...)&gt; {
        public:
          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;
          template&lt;class A&gt; function(allocator_arg_t, const A&amp;,
          const function&amp;);
          template&lt;class A&gt; function(allocator_arg_t, const A&amp;,
          function&amp;&amp;);
          template&lt;class F, class A&gt; function(allocator_arg_t, const A&amp;, F);
          // remainder unchanged
        };

        template&lt;class R, class... ArgTypes, class Alloc&gt;
          struct uses_allocator&lt;function&lt;R(ArgTypes...)&gt;, Alloc&gt;
            : true_type { };
      }
</code></pre>

<blockquote><p>2 When any <code>function</code> 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 Allocator (Table 17.6.3.5). <ins>Each such constructor has the same effects as the corresponding constructor without the <code>allocator_arg_t, const A&amp;,</code> parameters, except that a</ins><del>A</del> copy of the allocator argument is used to allocate memory, if necessary, for the internal data structures of the constructed <code>function</code> object.</p></blockquote>
</body></html>
