<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2108: No way to identify allocator types that always compare equal</title>
<meta property="og:title" content="Issue 2108: No way to identify allocator types that always compare equal">
<meta property="og:description" content="C++ library issue. Status: Resolved">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2108.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="2108"><a href="lwg-defects.html#2108">2108</a>. No way to identify allocator types that always compare equal</h3>
<p><b>Section:</b> 16.4.4.6 <a href="https://wg21.link/allocator.requirements">[allocator.requirements]</a> <b>Status:</b> <a href="lwg-active.html#Resolved">Resolved</a>
 <b>Submitter:</b> Jonathan Wakely <b>Opened:</b> 2011-12-01 <b>Last modified:</b> 2018-12-03</p>
<p><b>Priority: </b>3
</p>
<p><b>View other</b> <a href="lwg-index-open.html#allocator.requirements">active issues</a> in [allocator.requirements].</p>
<p><b>View all other</b> <a href="lwg-index.html#allocator.requirements">issues</a> in [allocator.requirements].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Resolved">Resolved</a> status.</p>
<p><b>Discussion:</b></p>

<p>
Whether two allocator objects compare equal affects the complexity of
container copy and move assignments and also the possibility of an
exception being thrown by container move assignments. The latter point
means container move assignment cannot be <code>noexcept</code> when
<code>propagate_on_container_move_assignment</code> (POCMA) is false for the
allocator because there is no way to detect at compile-time if two
allocators will compare equal. LWG <a href="lwg-defects.html#2013" title="Do library implementers have the freedom to add constexpr? (Status: C++14)">2013</a><sup><a href="https://cplusplus.github.io/LWG/issue2013" title="Latest snapshot">(i)</a></sup> means this affects all
containers using <code>std::allocator</code>, but even if that is resolved, this
affects all stateless allocators which do not explicitly define POCMA
to <code>true_type</code>.
<p/>
One solution would be to add an "always_compare_equal" trait to
<code>allocator_traits</code>, but that would be duplicating information that is
already defined by the type's equality operator if that operator
always returns true. Requiring users to write <code>operator==</code> that simply
returns true and also explicitly override a trait to repeat the same
information would be unfortunate and risk user errors that allow the
trait and actual <code>operator==</code> to disagree.
<p/>
Dave Abrahams suggested a better solution in message c++std-lib-31532,
namely to allow <code>operator==</code> to return <code>true_type</code>, which is convertible
to <code>bool</code> but also detectable at compile-time. Adopting this as the
recommended way to identify allocator types that always compare equal
only requires a slight relaxation of the allocator requirements so
that <code>operator==</code> is not required to return <code>bool</code> exactly.
<p/>
The allocator requirements do not make it clear that it is well-defined 
to compare non-const values, that should be corrected too.
<p/>
In message c++std-lib-31615 Pablo Halpern suggested an <code>always_compare_equal</code> 
trait that could still be defined, but with a sensible default value rather 
than requiring users to override it, and using that to set sensible values for 
other allocator traits:
</p>
<blockquote><p>
Do we still need <code>always_compare_equal</code> if we can have an <code>operator==</code>
that returns <code>true_type</code>?  What would its default value be? <code>is_empty&lt;A&gt;
|| is_convertible&lt;decltype(a == a), true_type&gt;::value</code>, perhaps?  One
benefit I see to such a definition is that stateless C++03 allocators
that don't use the <code>true_type</code> idiom will still benefit from the new
trait.
<p/>
[&hellip;]
<p/>
One point that I want to ensure doesn't get lost is that if we adopt some sort of 
<code>always_compare_equal</code>-like trait, then <code>propagate_on_container_swap</code> 
and <code>propagate_on_container_move_assignment</code> should default to 
<code>always_compare_equal</code>. Doing this will eliminate unnecessary requirements 
on the container element type, as per [LWG <a href="lwg-defects.html#2103" title="std::allocator_traits&lt;std::allocator&lt;T&gt;&gt;::propagate_on_container_move_assignment (Status: C++14)">2103</a><sup><a href="https://cplusplus.github.io/LWG/issue2103" title="Latest snapshot">(i)</a></sup>].
</p></blockquote>
<p>
Optionally, <code>operator==</code> for <code>std::allocator</code> could be made to return 
<code>true_type</code>, however if LWG <a href="lwg-defects.html#2103" title="std::allocator_traits&lt;std::allocator&lt;T&gt;&gt;::propagate_on_container_move_assignment (Status: C++14)">2103</a><sup><a href="https://cplusplus.github.io/LWG/issue2103" title="Latest snapshot">(i)</a></sup> is adopted that is less important.
<p/>
Alberto Ganesh Barbati: Suggest either <code>always_compare_equal</code>,
<code>all_objects_(are_)equivalent</code>, or <code>all_objects_compare_equal</code>.
</p>

<p><i>[2014-11-07 Urbana]</i></p>

<p>
Resolved by <a href="https://wg21.link/n4258">N4258</a>
</p>



<p id="res-2108"><b>Proposed resolution:</b></p>
<p>This wording is relative to the FDIS.</p>

<ol>
<li><p>Change Table 27 &mdash; "Descriptive variable definitions" in 16.4.4.6 <a href="https://wg21.link/allocator.requirements">[allocator.requirements]</a>:</p>

<table border="1">
<caption>Table 27 &mdash; Descriptive variable definitions</caption>
<tr>
<th>Variable</th>
<th>Definition</th>
</tr> 

<tr>
<td>
<code>a3<ins>, a4</ins></code>
</td>
<td>
<del>an rvalue of</del><ins>values of (possibly <code>const</code>)</ins> type <code>X</code>
</td>
</tr>

<tr>
<td>
<code>b</code>
</td>
<td>
a value of <ins>(possibly <code>const</code>)</ins> type <code>Y</code>
</td>
</tr>

</table>

</li>

<li><p>Change Table 28 &mdash; "Allocator requirements" in 16.4.4.6 <a href="https://wg21.link/allocator.requirements">[allocator.requirements]</a>:</p>

<table border="1">
<caption>Table 28 &mdash; Allocator requirements</caption>
<tr>
<th>Expression</th>
<th>Return type</th>
<th>Assertion&#47;note pre-&#47;post-condition</th>
<th>Default</th>
</tr> 

<tr>
<td>
<code><del>a1 == a2</del><ins>a3 == a4</ins></code>
</td>
<td>
<ins>convertible to</ins> <code>bool</code>
</td>
<td>
returns true only if storage<br/>
allocated from each can be<br/>
deallocated via the other.<br/>
<code>operator==</code> shall be reflexive,<br/>
symmetric, and transitive, and<br/>
shall not exit via an exception.
</td>
<td>
</td>
</tr>

<tr>
<td>
<code><del>a1 != a2</del><ins>a3 != a4</ins></code>
</td>
<td>
<ins>convertible to</ins> <code>bool</code>
</td>
<td>
same as <code><del>!(a1 == a2)</del><ins>!(a3 == a4)</ins></code>
</td>
<td>
</td>
</tr>

<tr>
<td>
<code>a<ins>3</ins> == b</code>
</td>
<td>
<ins>convertible to</ins> <code>bool</code>
</td>
<td>
same as <code>a<ins>3</ins> ==<br/>
Y::rebind&lt;T&gt;::other(b)</code>
</td>
<td>
</td>
</tr>

<tr>
<td>
<code>a<ins>3</ins> != b</code>
</td>
<td>
<ins>convertible to</ins> <code>bool</code>
</td>
<td>
same as <code>!(a<ins>3</ins> == b)</code>
</td>
<td>
</td>
</tr>

<tr>
<td colspan="4" align="center">
<code>[&hellip;]</code>
</td>
</tr>

<tr>
<td>
<code>a.select_on_-<br/>
container_copy_-<br/>
construction()</code>
</td>
<td>
<code>X</code>
</td>
<td>
Typically returns either <code>a</code> or<br/>
<code>X()</code>
</td>
<td>
<code>return a;</code>
</td>
</tr>

<tr>
<td>
<ins><code>X::always_compares_equal</code></ins>
</td>
<td>
<ins>Identical to or derived<br/>
from <code>true_type</code> or<br/>
<code>false_type</code></ins>
</td>
<td>
<ins><code>true_type</code> if the expression <code>x1 == x2</code> is<br/>
guaranteed to be <code>true</code> for any two (possibly<br/>
<code>const</code>) values <code>x1, x2</code> of type <code>X</code>, when<br/>
implicitly converted to <code>bool</code>. See Note B, below.</ins>
</td>
<td>
<ins><code>true_type</code>, if <code>is_empty&lt;X&gt;::value</code> is <code>true</code> or if<br/>
<code>decltype(declval&lt;const X&amp;&gt;() == declval&lt;const X&amp;&gt;())</code><br/> 
is convertible to <code>true_type</code>, otherwise <code>false_type</code>.</ins>
</td>
</tr>

<tr>
<td colspan="4" align="center">
<code>[&hellip;]</code>
</td>
</tr>

</table>
<p>
Note A: [&hellip;]
<p/>
<ins>Note B: If <code>X::always_compares_equal::value</code> or <code>XX::always_compares_equal::value</code> evaluate 
to <code>true</code> and an expression equivalent to <code>x1 == x2</code> or <code>x1 != x2</code> for any two values 
<code>x1, x2</code> of type <code>X</code> evaluates to <code>false</code> or <code>true</code>, respectively, the behaviour 
is undefined.</ins>
</p>

</li>

<li><p>Change class template <code>allocator_traits</code> synopsis, 20.2.9 <a href="https://wg21.link/allocator.traits">[allocator.traits]</a> as indicated:</p>

<blockquote><pre>
namespace std {
  template &lt;class Alloc&gt; struct allocator_traits {
    typedef Alloc allocator_type;
    [&hellip;]
    <ins>typedef <i>see below</i> always_compares_equal;</ins>
    typedef <i>see below</i> propagate_on_container_copy_assignment;
    [&hellip;]
  };
}
</pre></blockquote>
</li>

<li><p>Insert the following between 20.2.9.2 <a href="https://wg21.link/allocator.traits.types">[allocator.traits.types]</a> p6 and p7 as indicated:</p>

<blockquote><pre>
<ins>typedef <i>see below</i> always_compares_equal;</ins>
</pre><blockquote>
<p>
<ins>-?- <i>Type</i>: <code>Alloc::always_compares_equal</code> if such a type exists; otherwise, 
<code>true_type</code> if <code>is_empty&lt;Alloc&gt;::value</code> is <code>true</code> or if 
<code>decltype(declval&lt;const Alloc&amp;&gt;() == declval&lt;const Alloc&amp;&gt;())</code> 
is convertible to <code>true_type</code>; otherwise, <code>false_type</code>
.</ins>
</p>
</blockquote></blockquote>
<blockquote><pre>
typedef <i>see below</i> propagate_on_container_copy_assignment;
</pre><blockquote>
<p>
-7- <i>Type</i>: <code>Alloc::propagate_on_container_copy_assignment</code> if such a type exits, 
otherwise <code>false_type</code>.
</p>
</blockquote></blockquote>
</li>

<li><p>Change class template <code>allocator</code> synopsis, 20.2.10 <a href="https://wg21.link/default.allocator">[default.allocator]</a> as indicated:</p>

<blockquote><pre>
namespace std {
  template &lt;class T&gt; class allocator;

  <i>// specialize for <code>void</code>:</i>
  template &lt;&gt; class allocator&lt;void&gt; {
  public:
    typedef void* pointer;
    typedef const void* const_pointer;
    <i>// reference-to-<code>void</code> members are impossible.</i>
    typedef void value_type;
    template &lt;class U&gt; struct rebind { typedef allocator&lt;U&gt; other; };
  };

  template &lt;class T&gt; class allocator {
  public:
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T&amp; reference;
    typedef const T&amp; const_reference;
    typedef T value_type;
    template &lt;class U&gt; struct rebind { typedef allocator&lt;U&gt; other; };
    <ins>typedef true_type always_compares_equal;</ins>

    [&hellip;]
  };
}
</pre></blockquote>
</li>

</ol>






</body>
</html>
