|
| 1 | +<?xml version='1.0' encoding='utf-8' standalone='no'?> |
| 2 | +<!DOCTYPE issue SYSTEM "lwg-issue.dtd"> |
| 3 | + |
| 4 | +<issue num="4366" status="New"> |
| 5 | +<title>Heterogeneous comparison of `expected` may be ill-formed</title> |
| 6 | +<section> |
| 7 | +<sref ref="[expected.object.eq]"/><sref ref="[expected.void.eq]"/> |
| 8 | +</section> |
| 9 | +<submitter>Hewill Kang</submitter> |
| 10 | +<date>06 Sep 2025</date> |
| 11 | +<priority>99</priority> |
| 12 | + |
| 13 | +<discussion> |
| 14 | +<p> |
| 15 | +These comparison functions all explicitly `static_cast` the result of the underlying comparison to |
| 16 | +`bool`. However, the <i>Constraints</i> only require the implicit conversion, not the explicit one |
| 17 | +(i.e., "convertible to `bool`" rather than "models <code><i>boolean-testable</i></code>"). |
| 18 | +<p/> |
| 19 | +This means that in some pathological cases it will lead to hard errors |
| 20 | +(<a href="https://godbolt.org/z/b9ehzv3h4">demo</a>): |
| 21 | +</p> |
| 22 | +<blockquote><pre> |
| 23 | +#include <expected> |
| 24 | + |
| 25 | +struct E1 {}; |
| 26 | +struct E2 {}; |
| 27 | + |
| 28 | +struct Bool { |
| 29 | + operator bool() const; |
| 30 | + explicit operator bool() = delete; |
| 31 | +}; |
| 32 | +Bool operator==(E1, E2); |
| 33 | + |
| 34 | +int main() { |
| 35 | + std::unexpected e1{E1{}}; |
| 36 | + std::unexpected e2{E2{}}; |
| 37 | + return std::expected<int, E1>{e1} == e2; // <span style="color:#C80000;font-weight:bold">fire</span> |
| 38 | +} |
| 39 | +</pre></blockquote> |
| 40 | +<p> |
| 41 | +It is reasonable to specify return consistency with actual <i>Constraints</i>. |
| 42 | +</p> |
| 43 | +</discussion> |
| 44 | + |
| 45 | +<resolution> |
| 46 | +<p> |
| 47 | +This wording is relative to <paper num="N5014"/>. |
| 48 | +</p> |
| 49 | + |
| 50 | +<ol> |
| 51 | + |
| 52 | +<li><p>Modify <sref ref="[expected.object.eq]"/> as indicated:</p> |
| 53 | + |
| 54 | +<blockquote> |
| 55 | +<pre> |
| 56 | +template<class T2> friend constexpr bool operator==(const expected& x, const T2& v); |
| 57 | +</pre> |
| 58 | +<blockquote> |
| 59 | +<p> |
| 60 | +-3- <i>Constraints</i>: `T2` is not a specialization of `expected`. The expression `*x == v` is well-formed |
| 61 | +and its result is convertible to `bool`. |
| 62 | +<p/> |
| 63 | +[<i>Note 1</i>: <code>T</code> need not be <i>Cpp17EqualityComparable</i>. — <i>end note</i>] |
| 64 | +<p/> |
| 65 | +-4- <i>Returns</i>: <ins>If</ins> `x.has_value()` <ins>is `true`,</ins> |
| 66 | +<code><del>&& static_cast<bool>(</del>*x == v<del>)</del></code><ins>; otherwise `false`</ins>. |
| 67 | +</p> |
| 68 | +</blockquote> |
| 69 | +<pre> |
| 70 | +template<class E2> friend constexpr bool operator==(const expected& x, const unexpected<E2>& e); |
| 71 | +</pre> |
| 72 | +<blockquote> |
| 73 | +<p> |
| 74 | +-5- <i>Constraints</i>: The expression `x.error() == e.error()` is well-formed and its result is convertible |
| 75 | +to `bool`. |
| 76 | +<p/> |
| 77 | +-6- <i>Returns</i>: <ins>If</ins> `!x.has_value()` <ins>is `true`,</ins> |
| 78 | +<code><del>&& static_cast<bool>(</del>x.error() == e.error()<del>)</del></code><ins>; otherwise `false`</ins>. |
| 79 | +</p> |
| 80 | +</blockquote> |
| 81 | +</blockquote> |
| 82 | + |
| 83 | +</li> |
| 84 | + |
| 85 | +<li><p>Modify <sref ref="[expected.void.eq]"/> as indicated:</p> |
| 86 | + |
| 87 | +<blockquote> |
| 88 | +<pre> |
| 89 | +template<class T2, class E2> requires is_void_v<T2> |
| 90 | + friend constexpr bool operator==(const expected& x, const expected<T2, E2>& y); |
| 91 | +</pre> |
| 92 | +<blockquote> |
| 93 | +<p> |
| 94 | +-1- <i>Constraints</i>: The expression `x.error() == y.error()` is well-formed and its result |
| 95 | +is convertible to `bool`. |
| 96 | +<p/> |
| 97 | +-2- <i>Returns</i>: If `x.has_value()` does not equal `y.has_value()`, `false`; |
| 98 | +otherwise <ins>if</ins> `x.has_value()` <ins>is `true`, `true`; otherwise</ins> |
| 99 | +<code><del>|| static_cast<bool>(</del>x.error() == y.error()<del>)</del></code>. |
| 100 | +</p> |
| 101 | +</blockquote> |
| 102 | +<pre> |
| 103 | +template<class E2> |
| 104 | + friend constexpr bool operator==(const expected& x, const unexpected<E2>& e); |
| 105 | +</pre> |
| 106 | +<blockquote> |
| 107 | +<p> |
| 108 | +-3- <i>Constraints</i>: The expression `x.error() == e.error()` is well-formed and its |
| 109 | +result is convertible to `bool`. |
| 110 | +<p/> |
| 111 | +-4- <i>Returns</i>: <ins>If</ins> `!x.has_value()` <ins>is `true`,</ins> |
| 112 | +<code><del>&& static_cast<bool>(</del>x.error() == e.error()<del>)</del></code> |
| 113 | +<ins>; otherwise `false`</ins>. |
| 114 | +</p> |
| 115 | +</blockquote> |
| 116 | +</blockquote> |
| 117 | + |
| 118 | +</li> |
| 119 | + |
| 120 | +</ol></resolution> |
| 121 | + |
| 122 | +</issue> |
0 commit comments