<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 3775: Broken dependencies in the Cpp17Allocator requirements</title>
<meta property="og:title" content="Issue 3775: Broken dependencies in the Cpp17Allocator requirements">
<meta property="og:description" content="C++ library issue. Status: C++23">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue3775.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#C++23">C++23</a> status.</em></p>
<h3 id="3775"><a href="lwg-defects.html#3775">3775</a>. Broken dependencies in the <i>Cpp17Allocator</i> requirements</h3>
<p><b>Section:</b> 16.4.4.6.1 <a href="https://wg21.link/allocator.requirements.general">[allocator.requirements.general]</a> <b>Status:</b> <a href="lwg-active.html#C++23">C++23</a>
 <b>Submitter:</b> Alisdair Meredith <b>Opened:</b> 2022-09-22 <b>Last modified:</b> 2023-11-22</p>
<p><b>Priority: </b>Not Prioritized
</p>
<p><b>View other</b> <a href="lwg-index-open.html#allocator.requirements.general">active issues</a> in [allocator.requirements.general].</p>
<p><b>View all other</b> <a href="lwg-index.html#allocator.requirements.general">issues</a> in [allocator.requirements.general].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#C++23">C++23</a> status.</p>
<p><b>Discussion:</b></p>
<p>
This issue is extracted from <a href="https://wg21.link/P0177R2" title=" Cleaning up allocator_traits">P0177R2</a> as
that paper stalled on the author's ability to update in time for C++17.  While
the issue was recorded and going to be resolved in the paper, we did not file
an issue for the list when work on that paper stopped.
<p/>
Many of the types and expressions in the <i>Cpp17Allocator</i> requirements are
optional, and as such a default is provided that is exposed through
<code>std::allocator_traits</code>.  However, some types and operations are
specified directly in terms of the allocator member, when really they should be
specified allowing for reliance on the default, obtained through
<code>std::allocator_traits</code>.  For example, <code>X::pointer</code> is
an optional type and not required to exist; <code>XX::pointer</code> is either
<code>X::pointer</code> when it is present, or the default formula otherwise,
and so is guaranteed to always exist, and the intended interface for user code.
Observe that bullet list in p2, which acts as the key to the names in the
<i>Cpp17Allocator</i> requirements, gets this right, unlike most of the text
that follows.
<p/>
This change corresponds to the known implementations, which meet the intended
contract rather than that currently specified.  For example,
<code>std::allocator</code> does not provide any of the <code>pointer</code>
related typedef members, so many of the default semantics indicated today would
be ill-formed if implementations were not already implementing the fix.
<p/>
An alternative resolution might be to add wording around p1-3 to state that if
a name lookup fails then the default formula is used.  However, it is simply
clearer to write the constraints as intended, in the form of code that users
can write, rather than hide behind a layer of indirect semantics that may be
interpreted as requiring another layer of SFINAE metaprogramming.
</p>

<p><i>[2022-10-12; Reflector poll]</i></p>

<p>
Set status to Tentatively Ready after eight votes in favour during reflector poll.
</p>

<p><i>[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting &rarr; WP.]</i></p>



<p id="res-3775"><b>Proposed resolution:</b></p>
<p>This wording is relative to <a href="https://wg21.link/N4917" title=" Working Draft, Standard for Programming Language C++">N4917</a>.</p>

<ol>
<li>
<p>Modify 16.4.4.6.1 <a href="https://wg21.link/allocator.requirements.general">[allocator.requirements.general]</a> as indicated:</p>

<blockquote>
<pre>
typename X::pointer
</pre>
<blockquote>
<p>
-4- <i>Remarks</i>: Default: <code>T*</code>
</p>
</blockquote>
<pre>
typename X::const_pointer
</pre>
<blockquote>
<p>
-5- <i>Mandates</i>: <code>X<ins>X</ins>::pointer</code> is convertible to <code>X<ins>X</ins>::const_pointer</code>.
<p/>
-6- <i>Remarks</i>: Default: <code>pointer_traits&lt;X<ins>X</ins>::pointer&gt;::rebind&lt;const T&gt;</code>
</p>
</blockquote>
<pre>
typename X::void_pointer
typename Y::void_pointer
</pre>
<blockquote>
<p>
-7- <i>Mandates</i>: <code>X<ins>X</ins>::pointer</code> is convertible to <code>X<ins>X</ins>::void_pointer</code>. 
<code>X<ins>X</ins>::void_pointer</code> and <code>Y<ins>Y</ins>::void_pointer</code> are the same type.
<p/>
-8- <i>Remarks</i>: Default: <code>pointer_traits&lt;X<ins>X</ins>::pointer&gt;::rebind&lt;void&gt;</code>
</p>
</blockquote>
<pre>
typename X::const_void_pointer
typename Y::const_void_pointer
</pre>
<blockquote>
<p>
-9- <i>Mandates</i>: <code>X<ins>X</ins>::pointer</code>, <code>X<ins>X</ins>::const_pointer</code>, and 
<code>X<ins>X</ins>::void_pointer</code> are convertible to <code>X<ins>X</ins>::const_void_pointer</code>. 
<code>X<ins>X</ins>::const_void_pointer</code> and <code>Y<ins>Y</ins>::const_void_pointer</code> are the same type.
<p/>
-10- <i>Remarks</i>: Default: <code>pointer_traits&lt;X<ins>X</ins>::pointer&gt;::rebind&lt;const void&gt;</code>
</p>
</blockquote>
<pre>
typename X::value_type
</pre>
<blockquote>
<p>
-11- <i>Result</i>: Identical to <code>T</code>.
</p>
</blockquote>
<pre>
typename X::size_type
</pre>
<blockquote>
<p>
-12- <i>Result</i>: An unsigned integer type that can represent the size of the largest object 
in the allocation model.
<p/>
-13- <i>Remarks</i>: Default: <code>make_unsigned_t&lt;X<ins>X</ins>::difference_type&gt;</code>
</p>
</blockquote>
<pre>
typename X::difference_type
</pre>
<blockquote>
<p>
-14- <i>Result</i>: A signed integer type that can represent the difference between any two pointers 
in the allocation model.
<p/>
-15- <i>Remarks</i>: Default: <code>pointer_traits&lt;X<ins>X</ins>::pointer&gt;::difference_type</code>
</p>
</blockquote>
<pre>
typename X::template rebind&lt;U&gt;::other
</pre>
<blockquote>
<p>
-16- <i>Result</i>: <code>Y</code>
<p/>
-17- <i>Postconditions</i>: For all <code>U</code> (including <code>T</code>), 
<code>Y<ins>Y</ins>::<del>template rebind</del><ins>rebind_alloc</ins>&lt;T&gt;<del>::other</del></code> 
is <code>X</code>.
<p/>
-18- <i>Remarks</i>: If <code>Allocator</code> is a class template instantiation of the form 
<code>SomeAllocator&lt;T, Args&gt;</code>, where <code>Args</code> is zero or more type arguments, and 
<code>Allocator</code> does not supply a <code>rebind</code> member template, the standard 
<code>allocator_traits</code> template uses <code>SomeAllocator&lt;U, Args&gt;</code> in place of 
<code>Allocator::rebind&lt;U&gt;::other</code> by default. For allocator types that are not template 
instantiations of the above form, no default is provided.
<p/>
-19- [<i>Note 1</i>: The member class template <code>rebind</code> of <code>X</code> is effectively a 
typedef template. In general, if the name <code>Allocator</code> is bound to <code>SomeAllocator&lt;T&gt;</code>, 
then <code>Allocator::rebind&lt;U&gt;::other</code> is the same type as <code>SomeAllocator&lt;U&gt;</code>, where 
<code>SomeAllocator&lt;T&gt;::value_type</code> is <code>T</code> and <code>SomeAllocator&lt;U&gt;::value_type</code> 
is <code>U</code>. &mdash; <i>end note</i>]
</p>
</blockquote>
<p>
[&hellip;]
</p>
<pre>
static_cast&lt;X<ins>X</ins>::pointer&gt;(w)
</pre>
<blockquote>
<p>
-29- <i>Result</i>: <code>X<ins>X</ins>::pointer</code>
<p/>
-30- <i>Postconditions</i>: <code>static_cast&lt;X<ins>X</ins>::pointer&gt;(w) == p</code>.
</p>
</blockquote>
<pre>
static_cast&lt;X<ins>X</ins>::const_pointer&gt;(x)
</pre>
<blockquote>
<p>
-31- <i>Result</i>: <code>X<ins>X</ins>::const_pointer</code>
<p/>
-32- <i>Postconditions</i>: <code>static_cast&lt;X<ins>X</ins>::const_pointer&gt;(x) == q</code>.
</p>
</blockquote>
<pre>
pointer_traits&lt;X<ins>X</ins>::pointer&gt;::pointer_to(r)
</pre>
<blockquote>
<p>
-33- <i>Result</i>: <code>X<ins>X</ins>::pointer</code>
<p/>
-34- <i>Postconditions</i>: Same as <code>p</code>.
</p>
</blockquote>
<pre>
a.allocate(n)
</pre>
<blockquote>
<p>
-35- <i>Result</i>: <code>X<ins>X</ins>::pointer</code>
<p/>
[&hellip;]
</p>
</blockquote>
<pre>
a.allocate(n, y)
</pre>
<blockquote>
<p>
-40- <i>Result</i>: <code>X<ins>X</ins>::pointer</code>
<p/>
[&hellip;]
</p>
</blockquote>
<pre>
a.allocate_at_least(n)
</pre>
<blockquote>
<p>
-43- <i>Result</i>: <code>allocation_result&lt;X<ins>X</ins>::pointer&gt;</code>
<p/>
-44- <i>Returns</i>: <code>allocation_result&lt;X<ins>X</ins>::pointer&gt;{ptr, count}</code> where 
<code>ptr</code> is memory allocated for an array of count <code>T</code> and such an object 
is created but array elements are not constructed, such that <code>count = n</code>.
If <code>n == 0</code>, the return value is unspecified.
<p/>
[&hellip;]
</p>
</blockquote>
[&hellip;]
<pre>
a.max_size()
</pre>
<blockquote>
<p>
-50- <i>Result</i>: <code>X<ins>X</ins>::size_type</code>
<p/>
-51- <i>Returns</i>: The largest value <ins><code>n</code></ins> that can meaningfully be passed to 
<code><del>X::</del><ins>a.</ins>allocate(<ins>n</ins>)</code>.
<p/>
-52- <i>Remarks</i>: Default: <code>numeric_limits&lt;size_type&gt;::max() / sizeof(value_type)</code>
</p>
</blockquote>
[&hellip;]
<pre>
a == b
</pre>
<blockquote>
<p>
-59- <i>Result</i>: <code>bool</code>
<p/>
-60- <i>Returns</i>: <code>a == Y<ins>Y</ins>::rebind<ins>_alloc</ins>&lt;T&gt;<del>::other</del>(b)</code>.
</p>
</blockquote>
[&hellip;]
<p>
-92- An allocator type <code>X</code> shall meet the <i>Cpp17CopyConstructible</i> requirements (Table 33). 
The <code>X<ins>X</ins>::pointer</code>, <code>X<ins>X</ins>::const_pointer</code>, <code>X<ins>X</ins>::void_pointer</code>, 
and <code>X<ins>X</ins>::const_void_pointer</code> types shall meet the <i>Cpp17NullablePointer</i> requirements 
(Table 37). No constructor, comparison operator function, copy operation, move operation, or swap operation 
on these pointer types shall exit via an exception. <code>X<ins>X</ins>::pointer</code> and 
<code>X<ins>X</ins>::const_pointer</code> shall also meet the requirements for a <i>Cpp17RandomAccessIterator</i> 
(25.3.5.7) and the additional requirement that, when <code><del>a</del><ins>p</ins></code> and 
<code>(<del>a</del><ins>p</ins> + n)</code> are dereferenceable pointer values for some integral value <code>n</code>, 
<code>addressof(*(<del>a</del><ins>p</ins> + n)) == addressof(*<del>a</del><ins>p</ins>) + n</code> is <code>true</code>.
<p/>
-93- Let <code>x1</code> and <code>x2</code> denote objects of (possibly different) types <code>X<ins>X</ins>::void_pointer</code>, 
<code>X<ins>X</ins>::const_void_pointer</code>, <code>X<ins>X</ins>::pointer</code>, or <code>X<ins>X</ins>::const_pointer</code>. 
Then, <code>x1</code> and <code>x2</code> are equivalently-valued pointer values, if and only if both <code>x1</code> and 
<code>x2</code> can be explicitly converted to the two corresponding objects <code>px1</code> and <code>px2</code> of type 
<code>X<ins>X</ins>::const_pointer</code>, using a sequence of <code>static_cast</code>s using only these four types, and the 
expression <code>px1 == px2</code> evaluates to <code>true</code>.
<p/>
-94- Let <code>w1</code> and <code>w2</code> denote objects of type <code>X<ins>X</ins>::void_pointer</code>. Then for the expressions
</p>
<blockquote><pre>
w1 == w2
w1 != w2
</pre></blockquote>
<p>
either or both objects may be replaced by an equivalently-valued object of type 
<code>X<ins>X</ins>::const_void_pointer</code> with no change in semantics.
<p/>
-95- Let <code>p1</code> and <code>p2</code> denote objects of type <code>X<ins>X</ins>::pointer</code>. Then for the expressions
</p>
<blockquote><pre>
p1 == p2
p1 != p2
p1 &lt; p2
p1 &lt;= p2
p1 &gt;= p2
p1 &gt; p2
p1 - p2
</pre></blockquote>
<p>
either or both objects may be replaced by an equivalently-valued object of type <code>X<ins>X</ins>::const_pointer</code> 
with no change in semantics.
</p>
</blockquote>
</li>
</ol>





</body>
</html>
