@@ -130,6 +130,42 @@ inline constexpr bool is_move_assignable_or_void_v = is_void_or_v<T, std::is_mov
130130template <class From , class To >
131131inline constexpr bool is_nothrow_convertible_v = noexcept (static_cast <To>(std::declval<From>()));
132132
133+ template <class Lhs , class Rhs , class = void >
134+ struct is_equality_result_convertible_to_bool : std::false_type
135+ {
136+ };
137+
138+ template <class Lhs , class Rhs >
139+ struct is_equality_result_convertible_to_bool <Lhs, Rhs, std::void_t <decltype (std::declval<const Lhs &>() == std::declval<const Rhs &>())>>
140+ : std::is_convertible<decltype (std::declval<const Lhs &>() == std::declval<const Rhs &>()), bool >
141+ {
142+ };
143+
144+ template <class Lhs , class Rhs >
145+ inline constexpr bool is_equality_result_convertible_to_bool_v = is_equality_result_convertible_to_bool<Lhs, Rhs>::value;
146+
147+ constexpr bool implicitly_convert_to_bool (bool value) noexcept
148+ {
149+ return value;
150+ }
151+
152+ template <class Lhs , class Rhs , class = void >
153+ struct is_nothrow_equality_result_convertible_to_bool : std::false_type
154+ {
155+ };
156+
157+ template <class Lhs , class Rhs >
158+ struct is_nothrow_equality_result_convertible_to_bool <
159+ Lhs,
160+ Rhs,
161+ std::void_t <decltype (implicitly_convert_to_bool(std::declval<const Lhs &>() == std::declval<const Rhs &>()))>
162+ > : std::bool_constant<noexcept (implicitly_convert_to_bool(std::declval<const Lhs &>() == std::declval<const Rhs &>()))>
163+ {
164+ };
165+
166+ template <class Lhs , class Rhs >
167+ inline constexpr bool is_nothrow_equality_result_convertible_to_bool_v = is_nothrow_equality_result_convertible_to_bool<Lhs, Rhs>::value;
168+
133169} // namespace expected_detail
134170
135171template <class E >
@@ -2037,9 +2073,16 @@ class expected
20372073 }
20382074
20392075 template <class T2 , class E2 >
2040- [[nodiscard]] friend constexpr std::enable_if_t <!std::is_void_v<T2 >, bool > operator ==(
2041- const expected &x, const expected<T2 , E2 > &y
2042- ) noexcept (noexcept (*x == *y) && noexcept (x.error() == y.error()))
2076+ [[nodiscard]] friend constexpr std::enable_if_t <
2077+ !std::is_void_v<T2 > &&
2078+ expected_detail::is_equality_result_convertible_to_bool_v<T, T2 > &&
2079+ expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >,
2080+ bool
2081+ >
2082+ operator ==(const expected &x, const expected<T2 , E2 > &y) noexcept (
2083+ expected_detail::is_nothrow_equality_result_convertible_to_bool_v<T, T2 > &&
2084+ expected_detail::is_nothrow_equality_result_convertible_to_bool_v<E, E2 >
2085+ )
20432086 {
20442087 if (x.has_value () != y.has_value ())
20452088 {
@@ -2056,20 +2099,26 @@ class expected
20562099 }
20572100#if ZEUS_EXPECTED_CPLUSPLUS < 202'002L
20582101 template <class T2 , class E2 >
2059- [[nodiscard]] friend constexpr std::enable_if_t <!std::is_void_v<T2 >, bool > operator !=(
2060- const expected &x, const expected<T2 , E2 > &y
2061- ) noexcept (noexcept (x == y))
2102+ [[nodiscard]] friend constexpr std::enable_if_t <
2103+ !std::is_void_v<T2 > &&
2104+ expected_detail::is_equality_result_convertible_to_bool_v<T, T2 > &&
2105+ expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >,
2106+ bool
2107+ > operator !=(const expected &x, const expected<T2 , E2 > &y) noexcept (noexcept (x == y))
20622108 {
20632109 return !(x == y);
20642110 }
20652111#endif
20662112
20672113 template <class T2 >
2068- [[nodiscard]] friend constexpr bool operator ==(const expected &x, const T2 &v) noexcept (noexcept (*x == v))
2114+ [[nodiscard]] friend constexpr std::enable_if_t <
2115+ !expected_detail::is_specialization_v<T2 , zeus::expected> && expected_detail::is_equality_result_convertible_to_bool_v<T, T2 >,
2116+ bool
2117+ > operator ==(const expected &x, const T2 &v) noexcept (expected_detail::is_nothrow_equality_result_convertible_to_bool_v<T, T2 >)
20692118 {
20702119 if (x.has_value ())
20712120 {
2072- return static_cast < bool >( *x == v) ;
2121+ return *x == v;
20732122 }
20742123 else
20752124 {
@@ -2078,54 +2127,37 @@ class expected
20782127 }
20792128#if ZEUS_EXPECTED_CPLUSPLUS < 202'002L
20802129 template <class T2 >
2081- [[nodiscard]] friend constexpr bool operator !=(const expected &x, const T2 &v) noexcept (noexcept (x == v))
2130+ [[nodiscard]] friend constexpr std::enable_if_t <
2131+ !expected_detail::is_specialization_v<T2 , zeus::expected> && expected_detail::is_equality_result_convertible_to_bool_v<T, T2 >,
2132+ bool
2133+ > operator !=(const expected &x, const T2 &v) noexcept (noexcept (x == v))
20822134 {
20832135 return !(x == v);
20842136 }
2085- template <class T2 >
2086- [[nodiscard]] friend constexpr std::enable_if_t <!expected_detail::is_specialization_v<T2 , zeus::expected>, bool > operator ==(
2087- const T2 &v, const expected &x
2088- ) noexcept (noexcept (x == v))
2089- {
2090- return x == v;
2091- }
2092- template <class T2 >
2093- [[nodiscard]] friend constexpr std::enable_if_t <!expected_detail::is_specialization_v<T2 , zeus::expected>, bool > operator !=(
2094- const T2 &v, const expected &x
2095- ) noexcept (noexcept (x == v))
2096- {
2097- return x != v;
2098- }
20992137#endif
21002138
21012139 template <class E2 >
2102- [[nodiscard]] friend constexpr bool operator ==(const expected &x, const unexpected<E2 > &e) noexcept (noexcept (x.error() == e.error()))
2140+ [[nodiscard]] friend constexpr std::enable_if_t <expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >, bool > operator ==(
2141+ const expected &x, const unexpected<E2 > &e
2142+ ) noexcept (expected_detail::is_nothrow_equality_result_convertible_to_bool_v<E, E2 >)
21032143 {
21042144 if (x.has_value ())
21052145 {
21062146 return false ;
21072147 }
21082148 else
21092149 {
2110- return static_cast < bool >( x.error () == e.error () );
2150+ return x.error () == e.error ();
21112151 }
21122152 }
21132153#if ZEUS_EXPECTED_CPLUSPLUS < 202'002L
21142154 template <class E2 >
2115- [[nodiscard]] friend constexpr bool operator !=(const expected &x, const unexpected<E2 > &e) noexcept (noexcept (x == e))
2155+ [[nodiscard]] friend constexpr std::enable_if_t <expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >, bool > operator !=(
2156+ const expected &x, const unexpected<E2 > &e
2157+ ) noexcept (noexcept (x == e))
21162158 {
21172159 return !(x == e);
21182160 }
2119- template <class E2 >
2120- [[nodiscard]] friend constexpr bool operator ==(const unexpected<E2 > &e, const expected &x) noexcept (noexcept (x == e))
2121- {
2122- return x == e;
2123- }
2124- template <class E2 >
2125- [[nodiscard]] friend constexpr bool operator !=(const unexpected<E2 > &e, const expected &x) noexcept (noexcept (x == e))
2126- {
2127- return x != e;
2128- }
21292161#endif
21302162};
21312163
@@ -2743,60 +2775,100 @@ class expected<void, E>
27432775 }
27442776
27452777 template <class T2 , class E2 >
2746- [[nodiscard]] friend constexpr std::enable_if_t <std::is_void_v<T2 >, bool > operator ==(
2747- const expected &x, const expected<T2 , E2 > &y
2748- ) noexcept (noexcept (x.error() == y.error()))
2778+ [[nodiscard]] friend constexpr std::
2779+ enable_if_t <std::is_void_v<T2 > && expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >, bool >
2780+ operator ==(
2781+ const expected &x, const expected<T2 , E2 > &y
2782+ ) noexcept (expected_detail::is_nothrow_equality_result_convertible_to_bool_v<E, E2 >)
27492783 {
27502784 if (x.has_value () != y.has_value ())
27512785 {
27522786 return false ;
27532787 }
2788+ else if (x.has_value ())
2789+ {
2790+ return true ;
2791+ }
27542792 else
27552793 {
2756- return x.has_value () || static_cast < bool >(x. error () == y.error () );
2794+ return x.error () == y.error ();
27572795 }
27582796 }
27592797#if ZEUS_EXPECTED_CPLUSPLUS < 202'002L
27602798 template <class T2 , class E2 >
2761- [[nodiscard]] friend constexpr std::enable_if_t <std::is_void_v<T2 >, bool > operator !=(
2762- const expected &x, const expected<T2 , E2 > &y
2763- ) noexcept (noexcept (x == y))
2799+ [[nodiscard]] friend constexpr std::
2800+ enable_if_t <std::is_void_v<T2 > && expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >, bool > operator !=(
2801+ const expected &x, const expected<T2 , E2 > &y
2802+ ) noexcept (noexcept (x == y))
27642803 {
27652804 return !(x == y);
27662805 }
27672806#endif
27682807
27692808 template <class E2 >
2770- [[nodiscard]] friend constexpr bool operator ==(const expected &x, const unexpected<E2 > &e) noexcept (noexcept (x.error() == e.error()))
2809+ [[nodiscard]] friend constexpr std::enable_if_t <expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >, bool > operator ==(
2810+ const expected &x, const unexpected<E2 > &e
2811+ ) noexcept (expected_detail::is_nothrow_equality_result_convertible_to_bool_v<E, E2 >)
27712812 {
27722813 if (x.has_value ())
27732814 {
27742815 return false ;
27752816 }
27762817 else
27772818 {
2778- return static_cast < bool >( x.error () == e.error () );
2819+ return x.error () == e.error ();
27792820 }
27802821 }
27812822#if ZEUS_EXPECTED_CPLUSPLUS < 202'002L
27822823 template <class E2 >
2783- [[nodiscard]] friend constexpr bool operator !=(const expected &x, const unexpected<E2 > &e) noexcept (noexcept (x == e))
2824+ [[nodiscard]] friend constexpr std::enable_if_t <expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >, bool > operator !=(
2825+ const expected &x, const unexpected<E2 > &e
2826+ ) noexcept (noexcept (x == e))
27842827 {
27852828 return !(x == e);
27862829 }
2787- template <class E2 >
2788- [[nodiscard]] friend constexpr bool operator ==(const unexpected<E2 > &e, const expected &x) noexcept (noexcept (x == e))
2789- {
2790- return x == e;
2791- }
2792- template <class E2 >
2793- [[nodiscard]] friend constexpr bool operator !=(const unexpected<E2 > &e, const expected &x) noexcept (noexcept (x == e))
2794- {
2795- return x != e;
2796- }
27972830#endif
27982831};
27992832
2833+ #if ZEUS_EXPECTED_CPLUSPLUS < 202'002L
2834+ // Deduce the expected operand to reject conversions exposed by MSVC's permissive C++17 hidden-friend lookup.
2835+ template <class T , class E , class T2 >
2836+ [[nodiscard]] constexpr std::enable_if_t <
2837+ !std::is_void_v<T> &&
2838+ !expected_detail::is_specialization_v<T2 , zeus::expected> &&
2839+ expected_detail::is_equality_result_convertible_to_bool_v<T, T2 >,
2840+ bool
2841+ >
2842+ operator ==(const T2 &v, const expected<T, E> &x) noexcept (noexcept (x == v))
2843+ {
2844+ return x == v;
2845+ }
2846+
2847+ template <class T , class E , class T2 >
2848+ [[nodiscard]] constexpr std::enable_if_t <
2849+ !std::is_void_v<T> &&
2850+ !expected_detail::is_specialization_v<T2 , zeus::expected> &&
2851+ expected_detail::is_equality_result_convertible_to_bool_v<T, T2 >,
2852+ bool
2853+ >
2854+ operator !=(const T2 &v, const expected<T, E> &x) noexcept (noexcept (x == v))
2855+ {
2856+ return x != v;
2857+ }
2858+
2859+ template <class T , class E , class E2 , std::enable_if_t <expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >> * = nullptr >
2860+ [[nodiscard]] constexpr bool operator ==(const unexpected<E2 > &e, const expected<T, E> &x) noexcept (noexcept (x == e))
2861+ {
2862+ return x == e;
2863+ }
2864+
2865+ template <class T , class E , class E2 , std::enable_if_t <expected_detail::is_equality_result_convertible_to_bool_v<E, E2 >> * = nullptr >
2866+ [[nodiscard]] constexpr bool operator !=(const unexpected<E2 > &e, const expected<T, E> &x) noexcept (noexcept (x == e))
2867+ {
2868+ return x != e;
2869+ }
2870+ #endif
2871+
28002872// standalone swap for void value type
28012873template <class E , std::enable_if_t <std::is_move_constructible_v<E> && std::is_swappable_v<E>> * = nullptr >
28022874constexpr void swap (expected<void , E> &lhs, expected<void , E> &rhs) noexcept (noexcept (lhs.swap(rhs)))
0 commit comments