|
| 1 | +<?xml version='1.0' encoding='utf-8' standalone='no'?> |
| 2 | +<!DOCTYPE issue SYSTEM "lwg-issue.dtd"> |
| 3 | + |
| 4 | +<issue num="4325" status="New"> |
| 5 | +<title>`std::indirect`'s `operator==` still does not support incomplete types</title> |
| 6 | +<section> |
| 7 | +<sref ref="[indirect.relops]"/> |
| 8 | +<sref ref="[indirect.comp.with.t]"/> |
| 9 | +</section> |
| 10 | +<submitter>Hewill Kang</submitter> |
| 11 | +<date>24 Aug 2025</date> |
| 12 | +<priority>99</priority> |
| 13 | + |
| 14 | +<discussion> |
| 15 | +<p> |
| 16 | +`std::indirect`'s `operator== |
| 17 | +<a href="https://github.com/cplusplus/papers/issues/1680#issuecomment-2646604309">intentionally</a> |
| 18 | +uses <i>Mandates</i> instead of <i>Constraints</i> to support incomplete types. However, its |
| 19 | +function signature has the following `noexcept` specification: |
| 20 | +</p> |
| 21 | +<blockquote><pre> |
| 22 | +template<class U, class AA> |
| 23 | + constexpr bool operator==(const indirect& lhs, const indirect<U, AA>& rhs) |
| 24 | + noexcept(noexcept(*lhs == *rhs)); |
| 25 | +</pre></blockquote> |
| 26 | +<p> |
| 27 | +That is, we check whether the expression `*lhs == *rhs` throws, which unfortunately leads to |
| 28 | +the following hard error: |
| 29 | +</p> |
| 30 | +<blockquote><pre> |
| 31 | +struct Incomplete; |
| 32 | +static_assert(std::equality_comparable<std::indirect<Incomplete>>); |
| 33 | +// <span style="color:#C80000;font-weight:bold">hard error, no match for 'operator==' (operand types are 'const Incomplete' and 'const Incomplete')</span> |
| 34 | +</pre></blockquote> |
| 35 | +<p> |
| 36 | +This makes `operator==` not SFINAE-friendly for incomplete types, which defeats the purpose. |
| 37 | +<p/> |
| 38 | +Also, checking `noexcept(*lhs == *rhs)` seems insufficient because the result of `*lhs == *rhs` |
| 39 | +might still throw during conversion to `bool`. |
| 40 | +</p> |
| 41 | +</discussion> |
| 42 | + |
| 43 | +<resolution> |
| 44 | +<p> |
| 45 | +This wording is relative to <paper num="N5014"/>. |
| 46 | +</p> |
| 47 | + |
| 48 | +<blockquote class="note"> |
| 49 | +<p> |
| 50 | +[<i>Drafting note:</i>: We introduce the exposition-only function <tt><i>FUN</i></tt> below to mimic |
| 51 | +the implicit conversion to `bool`. As a drive-by effect this helps us simplifying (and clarifying, see |
| 52 | +LWG <iref ref="408"/>) the existing <i>Mandates</i> element] |
| 53 | +</p> |
| 54 | +</blockquote> |
| 55 | + |
| 56 | +<ol> |
| 57 | + |
| 58 | +<li><p>Modify <sref ref="[indirect.relops]"/> as indicated:</p> |
| 59 | + |
| 60 | +<blockquote> |
| 61 | +<pre> |
| 62 | +template<class U, class AA> |
| 63 | + constexpr bool operator==(const indirect& lhs, const indirect<U, AA>& rhs) |
| 64 | + noexcept(<del>noexcept(*lhs == *rhs)</del><ins><i>see below</i></ins>); |
| 65 | +</pre> |
| 66 | +<blockquote> |
| 67 | +<p> |
| 68 | +<ins>-?- Let <tt><i>FUN</i></tt> denote the exposition-only function</ins> |
| 69 | +</p> |
| 70 | +<blockquote><pre> |
| 71 | +<ins>bool <i>FUN</i>(bool) noexcept;</ins> |
| 72 | +</pre></blockquote> |
| 73 | +<p> |
| 74 | +-1- <i>Mandates</i>: The expression <tt><ins><i>FUN</i>(</ins>*lhs == *rhs<ins>)</ins></tt> is well-formed <del>and its result is convertible to `bool`</del>. |
| 75 | +<p/> |
| 76 | +-2- <i>Returns</i>: If `lhs` is valueless or `rhs` is valueless, |
| 77 | +`lhs.valueless_after_move() == rhs.valueless_after_move()`; otherwise `*lhs == *rhs`. |
| 78 | +<p/> |
| 79 | +<ins>-?- <i>Remarks</i>: The exception specification is equivalent to:</ins> |
| 80 | +</p> |
| 81 | +<blockquote><pre> |
| 82 | +<ins>requires (const T& lhs, const U& rhs) { { <i>FUN</i>(lhs == rhs) } noexcept; }</ins> |
| 83 | +</pre></blockquote> |
| 84 | +</blockquote> |
| 85 | +</blockquote> |
| 86 | + |
| 87 | +</li> |
| 88 | + |
| 89 | +<li><p>Modify <sref ref="[indirect.comp.with.t]"/> as indicated:</p> |
| 90 | + |
| 91 | +<blockquote> |
| 92 | +<pre> |
| 93 | +template<class U> |
| 94 | + constexpr bool operator==(const indirect& lhs, const U& rhs) noexcept(<del>noexcept(*lhs == rhs)</del><ins><i>see below</i></ins>); |
| 95 | +</pre> |
| 96 | +<blockquote> |
| 97 | +<p> |
| 98 | +<ins>-?- Let <tt><i>FUN</i></tt> denote the exposition-only function</ins> |
| 99 | +</p> |
| 100 | +<blockquote><pre> |
| 101 | +<ins>bool <i>FUN</i>(bool) noexcept;</ins> |
| 102 | +</pre></blockquote> |
| 103 | +<p> |
| 104 | +-1- <i>Mandates</i>: The expression <tt><ins><i>FUN</i>(</ins>*lhs == *rhs<ins>)</ins></tt> is well-formed <del>and its result is convertible to `bool`</del>. |
| 105 | +<p/> |
| 106 | +-2- <i>Returns</i>: If `lhs` is valueless, `false`; otherwise `*lhs == rhs`. |
| 107 | +<p/> |
| 108 | +<ins>-?- <i>Remarks</i>: The exception specification is equivalent to:</ins> |
| 109 | +</p> |
| 110 | +<blockquote><pre> |
| 111 | +<ins>requires (const T& lhs, const U& rhs) { { <i>FUN</i>(lhs == rhs) } noexcept; }</ins> |
| 112 | +</pre></blockquote> |
| 113 | +</blockquote> |
| 114 | +</blockquote> |
| 115 | + |
| 116 | +</li> |
| 117 | + |
| 118 | +</ol> |
| 119 | +</resolution> |
| 120 | + |
| 121 | +</issue> |
0 commit comments