<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 3530: BUILTIN-PTR-MEOW should not opt the type out of syntactic checks</title>
<meta property="og:title" content="Issue 3530: BUILTIN-PTR-MEOW should not opt the type out of syntactic checks">
<meta property="og:description" content="C++ library issue. Status: C++23">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue3530.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="3530"><a href="lwg-defects.html#3530">3530</a>. <code><i>BUILTIN-PTR-MEOW</i></code> should not opt the type out of syntactic checks</h3>
<p><b>Section:</b> 22.10.8.8 <a href="https://wg21.link/comparisons.three.way">[comparisons.three.way]</a>, 22.10.9 <a href="https://wg21.link/range.cmp">[range.cmp]</a> <b>Status:</b> <a href="lwg-active.html#C++23">C++23</a>
 <b>Submitter:</b> Tim Song <b>Opened:</b> 2021-03-04 <b>Last modified:</b> 2023-11-22</p>
<p><b>Priority: </b>Not Prioritized
</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>
The use of <code><i>BUILTIN-PTR-MEOW</i></code> for the constrained comparison
function objects was needed to disable the semantic requirements on the
associated concepts when the comparison resolves to a built-in operator
comparing pointers: the comparison object is adding special handling for this
case to produce a total order despite the core language saying otherwise,
so requiring the built-in operator to then produce a total order as part
of the semantic requirements doesn't make sense.
<p/>
However, because it is specified as a disjunction on the constraint,
it means that the comparison function objects are now required to accept
types that don't even meet the syntactic requirements of the associated
concept. For example, <code>ranges::less</code> requires all six comparison operators
(because of <code>totally_ordered_with</code>) to be present &hellip; except when
<code>operator&lt;</code> on the arguments resolves to a built-in operator comparing
pointers, in which case it just requires <code>operator&lt;</code> and <code>operator==</code>
(except that the latter isn't even required to be checked &mdash; it comes from the use
of <code>ranges::equal_to</code> in the precondition of <code>ranges::less</code>).
This seems entirely arbitrary.
</p>

<p><i>[2021-03-12; Reflector poll]</i></p>

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

<p><i>[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting &rarr; WP.]</i></p>



<p id="res-3530"><b>Proposed resolution:</b></p>
<p>
This wording is relative to <a href="https://wg21.link/n4878">N4878</a>.
</p>

<ol>
<li><p>Edit 22.10.8.8 <a href="https://wg21.link/comparisons.three.way">[comparisons.three.way]</a> as indicated:</p>
<blockquote>
<p>
<del>-1- In this subclause, <code><i>BUILTIN-PTR-THREE-WAY</i>(T, U)</code> for types <code>T</code>
and <code>U</code> is a boolean constant expression. <code><i>BUILTIN-PTR-THREE-WAY</i>(T, U)</code>
is true if and only if <code>&lt;=&gt;</code> in the expression</del>
</p>
<blockquote><pre>
<del>declval&lt;T&gt;() &lt;=&gt; declval&lt;U&gt;()</del>
</pre></blockquote>
<p><del>resolves to a built-in operator comparing pointers.</del></p>

<pre>
struct compare_three_way {
  template&lt;class T, class U&gt;
    <del>requires three_way_comparable_with&lt;T, U&gt; || <i>BUILTIN-PTR-THREE-WAY</i>(T, U)</del>
  constexpr auto operator()(T&amp;&amp; t, U&amp;&amp; u) const;

  using is_transparent = <i>unspecified</i>;
};
</pre>

<pre>
template&lt;class T, class U&gt;
  <del>requires three_way_comparable_with&lt;T, U&gt; || <i>BUILTIN-PTR-THREE-WAY</i>(T, U)</del>
constexpr auto operator()(T&amp;&amp; t, U&amp;&amp; u) const;
</pre>
<blockquote>
<p>
<ins>-?- <i>Constraints:</i> <code>T</code> and <code>U</code> satisfy <code>three_way_comparable_with</code>.</ins>
<p/>
-2- <i>Preconditions:</i> If the expression
<code>std​::​forward&lt;T&gt;(t) &lt;=&gt; std​::​forward&lt;U&gt;(u)</code> results in a call
to a built-in operator <code>&lt;=&gt;</code> comparing pointers of type <code>P</code>, the conversion
sequences from both <code>T</code> and <code>U</code> to <code>P</code> are equality-preserving
(18.2 <a href="https://wg21.link/concepts.equality">[concepts.equality]</a>)<ins>; otherwise, <code>T</code> and <code>U</code>
model <code>three_way_comparable_with</code></ins>.
<p/>
-3- <i>Effects:</i></p>
<ol style="list-style-type: none">
<li><p>
(3.1) &mdash; If the expression <code>std​::​forward&lt;T&gt;(t) &lt;=&gt; std​::​forward&lt;U&gt;(u)</code>
results in a call to a built-in operator <code>&lt;=&gt;</code> comparing pointers of type <code>P</code>,
returns <code>strong_­ordering​::​less</code> if (the converted value of) <code>t</code> precedes <code>u</code> in
the implementation-defined strict total order over pointers (3.28 <a href="https://wg21.link/defns.order.ptr">[defns.order.ptr]</a>),
<code>strong_­ordering​::​greater</code> if <code>u</code> precedes <code>t</code>, and otherwise <code>strong_­ordering​::​equal</code>.
</p>
</li>
<li><p>
(3.2) &mdash; Otherwise, equivalent to: <code>return std​::​forward&lt;T&gt;(t) &lt;=&gt; std​::​forward&lt;U&gt;(u);</code>
</p></li>
</ol>
</blockquote>
</blockquote>
</li>
<li><p>Edit 22.10.9 <a href="https://wg21.link/range.cmp">[range.cmp]</a> as indicated:</p>
<blockquote>
<p>
<del>-1- In this subclause, <code><i>BUILTIN-PTR-CMP</i>(T,</code> <i>op</i><code>, U)</code>
for types <code>T</code> and <code>U</code> and where <i>op</i> is an equality (7.6.10 <a href="https://wg21.link/expr.eq">[expr.eq]</a>)
or relational operator (7.6.9 <a href="https://wg21.link/expr.rel">[expr.rel]</a>) is a boolean constant expression.
<code><i>BUILTIN-PTR-CMP</i>(T,</code> <i>op</i><code>, U)</code>
is true if and only if <i>op</i> in the expression
<code>declval&lt;T&gt;()</code> <i>op</i> <code>declval&lt;U&gt;()</code>
resolves to a built-in operator comparing pointers.</del></p>

<pre>
struct ranges::equal_to {
  template&lt;class T, class U&gt;
    <del>requires equality_comparable_with&lt;T, U&gt; || <i>BUILTIN-PTR-CMP</i>(T, ==, U)</del>
  constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;

  using is_transparent = <i>unspecified</i>;
};
</pre>

<pre>
template&lt;class T, class U&gt;
  <del>requires equality_comparable_with&lt;T, U&gt; || <i>BUILTIN-PTR-CMP</i>(T, ==, U)</del>
constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;
</pre>
<blockquote>
<p>
<ins>-?- <i>Constraints:</i> <code>T</code> and <code>U</code> satisfy <code>equality_comparable_with</code>.</ins>
<p/>
-2- <i>Preconditions:</i> If the expression
<code>std​::​forward&lt;T&gt;(t) == std​::​forward&lt;U&gt;(u)</code> results in a call
to a built-in operator <code>==</code> comparing pointers of type <code>P</code>, the conversion
sequences from both <code>T</code> and <code>U</code> to <code>P</code> are equality-preserving
(18.2 <a href="https://wg21.link/concepts.equality">[concepts.equality]</a>)<ins>; otherwise, <code>T</code> and <code>U</code>
model <code>equality_comparable_with</code></ins>.
<p/>
-3- <i>Effects:</i></p>
<ol style="list-style-type: none">
<li><p>
(3.1) &mdash; If the expression <code>std​::​forward&lt;T&gt;(t) == std​::​forward&lt;U&gt;(u)</code>
results in a call to a built-in operator <code>==</code> comparing pointers of type <code>P</code>,
returns <code>false</code> if either (the converted value of) <code>t</code> precedes <code>u</code> or <code>u</code>
precedes <code>t</code> in the implementation-defined strict total order over pointers
(3.28 <a href="https://wg21.link/defns.order.ptr">[defns.order.ptr]</a>) and otherwise <code>true</code>.
</p>
</li>
<li><p>
(3.2) &mdash; Otherwise, equivalent to: <code>return std​::​forward&lt;T&gt;(t) == std​::​forward&lt;U&gt;(u);</code>
</p>
</li>
</ol>
</blockquote>
<pre>
struct ranges::not_equal_to {
    template&lt;class T, class U&gt;
        <del>requires equality_comparable_with&lt;T, U&gt; || <i>BUILTIN-PTR-CMP</i>(T, ==, U)</del>
    constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;

    using is_transparent = <i>unspecified</i>;
};
</pre>

<pre>
<ins>template&lt;class T, class U&gt;
constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;</ins>
</pre>
<blockquote>
<p>
<ins>-?- <i>Constraints:</i> <code>T</code> and <code>U</code> satisfy <code>equality_comparable_with</code>.</ins>
<p/>
-4- <del><code>operator()</code> has effects e</del><ins><i>Effects:</i> E</ins>quivalent to:</p>
<blockquote><pre>
return !ranges::equal_to{}(std::forward&lt;T&gt;(t), std::forward&lt;U&gt;(u));
</pre></blockquote>
</blockquote>
<pre>
struct ranges::greater {
  template&lt;class T, class U&gt;
    <del>requires totally_ordered_with&lt;T, U&gt; || <i>BUILTIN-PTR-CMP</i>(T, &lt;, U)</del>
  constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;

  using is_transparent = <i>unspecified</i>;
};
</pre>

<pre>
template&lt;class T, class U&gt;
constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;
</pre>
<blockquote>
<p>
<ins>-?- <i>Constraints:</i> <code>T</code> and <code>U</code> satisfy <code>totally_ordered_with</code>.</ins>
<p/>
-5- <del><code>operator()</code> has effects e</del><ins><i>Effects:</i> E</ins>quivalent to:</p>
<blockquote><pre>
return ranges::less{}(std::forward&lt;U&gt;(u), std::forward&lt;T&gt;(t));
</pre></blockquote>
</blockquote>

<pre>
struct ranges::less {
  template&lt;class T, class U&gt;
    <del>requires totally_ordered_with&lt;T, U&gt; || <i>BUILTIN-PTR-CMP</i>(T, &lt;, U)</del>
  constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;

  using is_transparent = <i>unspecified</i>;
};
</pre>

<pre>
template&lt;class T, class U&gt;
  <del>requires totally_ordered_with&lt;T, U&gt; || <i>BUILTIN-PTR-CMP</i>(T, &lt;, U)</del>
constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;
</pre>
<blockquote>
<p>
<ins>-?- <i>Constraints:</i> <code>T</code> and <code>U</code> satisfy <code>totally_ordered_with</code>.</ins>
<p/>
-6- <i>Preconditions:</i> If the expression
<code>std​::​forward&lt;T&gt;(t) &lt; std​::​forward&lt;U&gt;(u)</code> results in a call
to a built-in operator <code>&lt;</code> comparing pointers of type <code>P</code>, the conversion
sequences from both <code>T</code> and <code>U</code> to <code>P</code> are equality-preserving
(18.2 <a href="https://wg21.link/concepts.equality">[concepts.equality]</a>)<ins>; otherwise, <code>T</code> and <code>U</code>
model <code>totally_ordered_with</code></ins>.
For any expressions <code>ET</code> and <code>EU</code> such that <code>decltype((ET))</code> is <code>T</code> and
<code>decltype((EU))</code> is <code>U</code>, exactly one of <code>ranges::less{}(ET, EU)</code>, <code>ranges::less{}(EU, ET)</code>,
or <code>ranges::equal_to{}(ET, EU)</code> is <code>true</code>.
<p/>
-7- <i>Effects:</i></p>
<ol style="list-style-type: none">
<li><p>
(7.1) &mdash; If the expression <code>std​::​forward&lt;T&gt;(t) &lt; std​::​forward&lt;U&gt;(u)</code>
results in a call to a built-in operator <code>&lt;</code> comparing pointers of type <code>P</code>,
returns <code>true</code> if (the converted value of) <code>t</code> precedes <code>u</code> in the
implementation-defined strict total order over pointers (3.28 <a href="https://wg21.link/defns.order.ptr">[defns.order.ptr]</a>)
and otherwise <code>false</code>.
</p>
</li>
<li><p>
(7.2) &mdash; Otherwise, equivalent to: <code>return std​::​forward&lt;T&gt;(t) &lt; std​::​forward&lt;U&gt;(u);</code>
</p>
</li>
</ol>
</blockquote>
<pre>
struct ranges::greater_equal {
  template&lt;class T, class U&gt;
    <del>requires totally_ordered_with&lt;T, U&gt; || <i>BUILTIN-PTR-CMP</i>(T, &lt;, U)</del>
  constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;

  using is_transparent = <i>unspecified</i>;
};
</pre>

<pre>
<ins>template&lt;class T, class U&gt;
constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;</ins>
</pre>
<blockquote>
<p>
<ins>-?- <i>Constraints:</i> <code>T</code> and <code>U</code> satisfy <code>totally_ordered_with</code>.</ins>
<p/>
-8- <del><code>operator()</code> has effects e</del><ins><i>Effects:</i> E</ins>quivalent to:</p>
<blockquote><pre>
return !ranges::less{}(std::forward&lt;T&gt;(t), std::forward&lt;U&gt;(u));
</pre></blockquote>
</blockquote>

<pre>
struct ranges::less_equal {
  template&lt;class T, class U&gt;
    <del>requires totally_ordered_with&lt;T, U&gt; || <i>BUILTIN-PTR-CMP</i>(T, &lt;, U)</del>
  constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;

  using is_transparent = <i>unspecified</i>;
};
</pre>

<pre>
<ins>template&lt;class T, class U&gt;
constexpr bool operator()(T&amp;&amp; t, U&amp;&amp; u) const;</ins>
</pre>
<blockquote>
<p>
<ins>-?- <i>Constraints:</i> <code>T</code> and <code>U</code> satisfy <code>totally_ordered_with</code>.</ins>
<p/>
-9- <del><code>operator()</code> has effects e</del><ins><i>Effects:</i> E</ins>quivalent to:</p>
<blockquote><pre>
return !ranges::less{}(std::forward&lt;U&gt;(u), std::forward&lt;T&gt;(t));
</pre></blockquote>
</blockquote>
</blockquote>
</li>
</ol>





</body>
</html>
