<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <title>P2652R0: Disallow user specialization of allocator_traits</title>
  <style>
    html {
      line-height: 1.5;
      font-family: Georgia, serif;
      font-size: 20px;
      color: #1a1a1a;
      background-color: #fdfdfd;
    }
    body {
      margin: 0 auto;
      max-width: 36em;
      padding-left: 50px;
      padding-right: 50px;
      padding-top: 50px;
      padding-bottom: 50px;
      hyphens: auto;
      word-wrap: break-word;
      text-rendering: optimizeLegibility;
      font-kerning: normal;
    }
    @media (max-width: 600px) {
      body {
        font-size: 0.9em;
        padding: 1em;
      }
    }
    @media print {
      body {
        background-color: transparent;
        color: black;
        font-size: 12pt;
      }
      p, h2, h3 {
        orphans: 3;
        widows: 3;
      }
      h2, h3, h4 {
        page-break-after: avoid;
      }
    }
    p {
      margin: 1em 0;
    }
    a {
      color: #1a1a1a;
    }
    a:visited {
      color: #1a1a1a;
    }
    img {
      max-width: 100%;
    }
    h1, h2, h3, h4, h5, h6 {
      margin-top: 1.4em;
    }
    h5, h6 {
      font-size: 1em;
      font-style: italic;
    }
    h6 {
      font-weight: normal;
    }
    ol, ul {
      padding-left: 1.7em;
      margin-top: 1em;
    }
    li > ol, li > ul {
      margin-top: 0;
    }
    blockquote {
      margin: 1em 0 1em 1.7em;
      padding-left: 1em;
      border-left: 2px solid #e6e6e6;
      color: #606060;
    }
    code {
      font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace;
      font-size: 85%;
      margin: 0;
    }
    pre {
      margin: 1em 0;
      overflow: auto;
    }
    pre code {
      padding: 0;
      overflow: visible;
    }
    .sourceCode {
     background-color: transparent;
     overflow: visible;
    }
    hr {
      background-color: #1a1a1a;
      border: none;
      height: 1px;
      margin: 1em 0;
    }
    table {
      margin: 1em 0;
      border-collapse: collapse;
      width: 100%;
      overflow-x: auto;
      display: block;
      font-variant-numeric: lining-nums tabular-nums;
    }
    table caption {
      margin-bottom: 0.75em;
    }
    tbody {
      margin-top: 0.5em;
      border-top: 1px solid #1a1a1a;
      border-bottom: 1px solid #1a1a1a;
    }
    th {
      border-top: 1px solid #1a1a1a;
      padding: 0.25em 0.5em 0.25em 0.5em;
    }
    td {
      padding: 0.125em 0.5em 0.25em 0.5em;
    }
    header {
      margin-bottom: 4em;
      text-align: center;
    }
    #TOC li {
      list-style: none;
    }
    #TOC a:not(:hover) {
      text-decoration: none;
    }
    code{white-space: pre-wrap;}
    span.smallcaps{font-variant: small-caps;}
    span.underline{text-decoration: underline;}
    div.column{display: inline-block; vertical-align: top; width: 50%;}
    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
    ul.task-list{list-style: none;}
    .display.math{display: block; text-align: center; margin: 0.5rem auto;}
  </style>
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
<header id="title-block-header">
<h1 class="title">P2652R0: Disallow user specialization of allocator_traits</h1>
</header>
<head>
<style type="text/css">
  body { max-width: 54em; }
  ins { text-decoration:none; background-color:#A0FFA0 }
  .new { text-decoration:none; background-color:#D0FFD0 }
  del { text-decoration:line-through; background-color:#FFA0A0 }
  strong { font-weight: inherit; color: #2020ff }
  table, td, th { border: 1px solid black; border-collapse:collapse; padding: 5px }
  blockquote { margin: 1em 0 1em 1.7em; padding-left: 1em; border-left: 0; }
  pre { left-margin: 50px; line-height: 1.1; }
  pre code { font-size: 80%; }
  del pre code { text-decoration: inherit; background-color:#FFA0A0 }
  ins pre code { text-decoration: inherit; background-color:#A0FFA0 }
</style>
</head>
<p>Pablo Halpern &lt;<a href="mailto:phalpern@halpernwightsoftware.com" class="email">phalpern@halpernwightsoftware.com</a>&gt;<br />
<!-- $TimeStamp$ -->2022-10-10 15:39 EDT<!-- $ --><br />
Target audience: LEWG/LWG</p>
<h1 id="abstract">Abstract</h1>
<p>The <code>allocator_traits</code> class template was introduced in C++11 with two goals in mind: 1) Provide default implementations for allocator types and operations, thus minimizing the requirements on allocators [allocator.requirements], and 2) provide a mechanism by which future standards could extend the allocator interface without changing allocator requirements and thus obsoleting existing allocators. The latter goal is undermined, however, by the standard currently allowing user-defined specializations of <code>std::allocator_traits</code>. Although the standard requires that any such specialization conform to the standard interface, it is not practical to change the standard interface – even by extending it – without breaking any existing user specializations. Indeed, the Sep 2022 C++23 CD, <a href="https://www.open-std.org/JTC1/SC22/WG21/prot/14882fdis/n4919.pdf">N4919</a> contains an extension, <code>allocate_at_least</code>, that logically belongs in <code>std:::allocator_traits</code>, but is expressed as an unrelated function because of the problem of potential user-defined specializations.</p>
<p>This paper proposes two possible solutions to this problem: 1) remove the user’s latitude for specializing <code>std::allocator_traits</code> or 2) deprecate <code>std::allocator_traits</code> entirely in favor of an “unbundled” set of allocator-interface functions.</p>
<p>This paper is the proposed resolution to a US NB comment having the same title.</p>
<h1 id="motivation">Motivation</h1>
<p>The minimal interface for a type conforming to the allocator requirements is that it have a <code>value_type</code> type, <code>allocate</code> and <code>deallocate</code> member functions, and equality comparison operators. The <code>allocator_traits</code> class template provides many other types and functions such as <code>pointer</code>, <code>rebind</code>, and <code>construct</code>. Generic types that use allocators are required to access the allocator through <code>std::allocator_traits</code>. The latter requirement was intended to allow the allocator interface to be extended without necessarily changing every existing allocator.</p>
<p>For example, C++03 allocators did not have a <code>void_pointer</code> member, but such a member is provided automatically through <code>allocator_traits</code>; an allocator class can override the default provided by <code>allocator_traits</code>, but is not required to do so.</p>
<p>The Standard description for each trait <code>X</code> in <code>std::allocator_traits&lt;A&gt;</code> typically follows the form, “<code>a.X</code> if that expression is well-formed; otherwise <em>some default</em>.” There is never any reason to specialize <code>std::allocator_traits</code> because any trait can be overridden simply by defining the appropriate member within the specific allocator class template. Unfortunately, the standard allows such user specialization and even implies that it is a reasonable thing to do. This allowance prevents the Standards Committee from adding new members to <code>std::allocator_traits</code> without breaking existing user specializations.</p>
<p>In <a href="http://wg21.link/P0401R1">P0401R1</a>, <code>allocate_at_least</code> was proposed as a static member of <code>std::allocator_traits</code> but it was changed to a free function in <a href="http://wg21.link/P0401R2">P0401R2</a> following a poll in LEWG in Cologne after it was pointed out that, because <code>std::allocator_traits</code> can be specialized and that existing specializations would not have the <code>allocate_at_least</code> member. It is this free function that is now in the September 2022 C++23 CD, <a href="https://www.open-std.org/JTC1/SC22/WG21/prot/14882fdis/n4919.pdf">N4919</a>. The current state of affairs, then, is that accessing an allocator is starting to become a hodgepodge of <code>std::allocator_traits</code> member-function calls and free-function calls. Before we standardize C++23, we should make an attempt to prevent this divergence.</p>
<h1 id="proposed-resolution-1-of-2">Proposed resolution 1 of 2</h1>
<p>This proposed resolution would disallow user specializations of <code>std::allocator_traits</code>. This change would be a breaking one, as existing specializations would become non-conforming. However, with the exception of the new <code>allocate_at_least</code> feature, existing code should continue to work for the time being. It is expected that specializations of <code>std::allocator_traits</code> are very rare, so the amount of potential breakage should be quite limited.</p>
<h2 id="wording-for-pr-1">Wording for PR 1:</h2>
<p>Modify section 16.4.4.6.1 [allocator.requirements.general], paragraph 3 as follows:</p>
<blockquote>
<p>The class template <code>allocator_traits</code> (20.2.8) supplies a uniform interface to all allocator types. This subclause describes the requirements on allocator types and thus on types used to instantiate <code>allocator_traits</code>. A requirement is optional if a default for a given type or expression is specified. Within the standard library <code>allocator_traits</code> template, an optional requirement that is not supplied by an allocator is replaced by the specified default type or expression. <del>A user specialization of allocator_traits may provide different defaults and may provide defaults for different requirements than the primary template.</del> <ins>If a program declares an explicit or partial specialization of <code>allocator_traits</code>, the program is ill-formed, no diagnostic required.</ins></p>
</blockquote>
<p>And Paragraph 46 as follows:</p>
<blockquote>
<p><em>Remarks</em>: <del>An allocator need not <code>support allocate_at_least</code>, but no default is provided in <code>allocator_traits</code>. If an allocator has an <code>allocate_at_least</code> member, it shall satisfy the requirements.</del> <ins>Default: <code>{a.allocate(n), n}</code>. </ins></p>
</blockquote>
<p>In section 20.2.2 [memory.syn], remove the non-member declaration for <code>allocate_at_least</code>:</p>
<del>
<pre><code>template&lt;class Allocator&gt;
  [[nodiscard]] constexpr allocation_result&lt;typename allocator_traits&lt;Allocator&gt;::pointer&gt;
    allocate_at_least(Allocator&amp; a, size_t n);                   // freestanding</code></pre>
</del>
<p>In section 20.2.9.1 [allocator.traits.general], add a new member in <code>allocator_traits</code>, probably immediately before <code>deallocate</code>:</p>
<ins>
<pre><code>template&lt;class Allocator&gt;
  [[nodiscard]] static constexpr allocation_result&lt;pointer&gt;
    allocate_at_least(Allocator&amp; a, size_t n);</code></pre>
</ins>
<p>And in section 20.2.9.3 [allocator.traits.members], add its definition</p>
<ins>
<pre><code>template&lt;class Allocator&gt;
  [[nodiscard]] static constexpr allocation_result&lt;pointer&gt;
    allocate_at_least(Allocator&amp; a, size_t n);</code></pre>
</ins>
<blockquote>
<ins>
<em>Returns</em>: <code>a.allocate_at_least(n)</code> if that expression is well-formed; otherwise, <code>{a.allocate(n), n}</code>.
</ins>
</blockquote>
<p>Finally, in section 20.2.9.4 [allocator.traits.other] paragraph 2, remove the definition of non-member <code>allocate_at_least</code>:</p>
<del>
<pre><code>template&lt;class Allocator&gt;
  [[nodiscard]] constexpr allocation_result&lt;typename allocator_traits&lt;Allocator&gt;::pointer&gt;
    allocate_at_least(Allocator&amp; a, size_t n);</code></pre>
</del>
<blockquote>
<del>
<em>Returns</em>: <code>a.allocate_at_least(n)</code> if that expression is well-formed; otherwise, <code>{a.allocate(n), n}</code>.
</del>
</blockquote>
<h1 id="proposed-resolution-2-of-2">Proposed resolution 2 of 2</h1>
<p>Another possible resolution is to move <em>everything</em> in the direction of <code>allocate_at_least</code>, i.e., deprecate <code>allocator_traits</code> and replace it by a set of namespace-scoped alias templates and free function templates. Such a change is too large for C++23, but if we can agree on this direction now, it can be made as a non-breaking change in C++26.</p>
<p>Complete proposed wording is not supplied herein, but the general idea is that each type in <code>allocator_traits</code> would be replaced by a type trait or free function, similar to the following:</p>
<ins>
<code>template &lt;class Alloc&gt; using allocator_pointer =</code> <em>see below</em> <code>;</code>
</ins>
<blockquote>
<ins>
<em>Type</em>: <code>Alloc::pointer</code> if the qualified-id <code>Alloc::pointer</code> is valid and denotes a type (13.10.3); otherwise, <code>allocator_value_type&lt;Alloc&gt;*</code>.
</ins>
</blockquote>
<ins>
<pre><code>template &lt;class Alloc, class T&gt;
  constexpr void allocator_destroy(Alloc&amp; a, T* p);</code></pre>
</ins>
<blockquote>
<ins>
<em>Effects</em>: Calls <code>a.destroy(p)</code> if that call is well-formed; otherwise, invokes <code>destroy_at(p)</code>.
</ins>
</blockquote>
<p>Repeat for each trait in <code>allocator_traits</code> change the description of how allocator-aware containers use allocators to use these new versions. The existing <code>allocator_traits</code> template would be moved into Appendix D and each member would be specified in terms of the new traits.</p>
<h1 id="conclusion">Conclusion</h1>
<p>The status quo would have allocator operations specified as a mixture of <code>allocator_traits</code> members and namespace-scope traits. We should decide which is our preferred method of specification. If we prefer a set of loosely related namespace-scope traits, then nothing needs to be done today except deciding this direction for the future. If, however, we prefer to use <code>allocator_traits</code>, then user specializations must be disallowed in C++23, <em>before</em> the standard starts drifting in the other direction.</p>
<h1 id="references">References</h1>
<p><a href="https://www.open-std.org/JTC1/SC22/WG21/prot/14882fdis/n4919.pdf">N4919</a>: Programming Languages – C++, Committee Draft, 2022-09-05.</p>
<p><a href="http://wg21.link/P0401">P0401R6</a>: Providing size feedback in the Allocator interface, Jonathan Wakely and Chris Kennelly, 2021-01-22</p>
</body>
</html>
