{ __t == __u } -> convertible_to<bool>;
{ __e == __e2 } -> convertible_to<bool>;
}
+ [[nodiscard]]
friend constexpr bool
operator==(const expected& __x, const expected<_Up, _Er2>& __y)
noexcept(noexcept(bool(*__x == *__y))
&& noexcept(bool(__x.error() == __y.error())))
{
+ if (__x.has_value() != __y.has_value())
+ return false;
if (__x.has_value())
- return __y.has_value() && bool(*__x == *__y);
- else
- return !__y.has_value() && bool(__x.error() == __y.error());
+ return *__x == *__y;
+ return __x.error() == __y.error();
}
template<typename _Up, same_as<_Tp> _Vp>
&& requires (const _Tp& __t, const _Up& __u) {
{ __t == __u } -> convertible_to<bool>;
}
+ [[nodiscard]]
friend constexpr bool
operator==(const expected<_Vp, _Er>& __x, const _Up& __v)
noexcept(noexcept(bool(*__x == __v)))
- { return __x.has_value() && bool(*__x == __v); }
+ {
+ if (__x.has_value())
+ return *__x == __v;
+ return false;
+ }
template<typename _Er2>
requires requires (const _Er& __e, const _Er2& __e2) {
{ __e == __e2 } -> convertible_to<bool>;
}
+ [[nodiscard]]
friend constexpr bool
operator==(const expected& __x, const unexpected<_Er2>& __e)
noexcept(noexcept(bool(__x.error() == __e.error())))
- { return !__x.has_value() && bool(__x.error() == __e.error()); }
+ {
+ if (!__x.has_value())
+ return __x.error() == __e.error();
+ return false;
+ }
friend constexpr void
swap(expected& __x, expected& __y)
&& requires (const _Er& __e, const _Er2& __e2) {
{ __e == __e2 } -> convertible_to<bool>;
}
+ [[nodiscard]]
friend constexpr bool
operator==(const expected& __x, const expected<_Up, _Er2>& __y)
noexcept(noexcept(bool(__x.error() == __y.error())))
{
+ if (__x.has_value() != __y.has_value())
+ return false;
if (__x.has_value())
- return __y.has_value();
- else
- return !__y.has_value() && bool(__x.error() == __y.error());
+ return true;
+ return __x.error() == __y.error();
}
template<typename _Er2>
requires requires (const _Er& __e, const _Er2& __e2) {
{ __e == __e2 } -> convertible_to<bool>;
}
+ [[nodiscard]]
friend constexpr bool
operator==(const expected& __x, const unexpected<_Er2>& __e)
noexcept(noexcept(bool(__x.error() == __e.error())))
- { return !__x.has_value() && bool(__x.error() == __e.error()); }
+ {
+ if (!__x.has_value())
+ return __x.error() == __e.error();
+ return false;
+ }
friend constexpr void
swap(expected& __x, expected& __y)
std::expected<int, int> e2;
VERIFY(e2 == e2);
VERIFY(e1 == e2);
+ VERIFY(e2 == e1);
VERIFY(e1 != std::unexpected<int>(1));
+
e1 = std::unexpected<int>(1);
VERIFY(e1 == std::unexpected<int>(1));
VERIFY(e1 != std::unexpected<int>(2));
VERIFY(e1 != e2);
+ VERIFY(e2 != e1);
+ VERIFY(e1 != 1);
+
+ e2 = std::unexpected<int>(1);
+ VERIFY(e1 == e2);
+ VERIFY(e2 == e1);
+
+ e2 = std::unexpected<int>(2);
+ VERIFY(e1 != e2);
+ VERIFY(e2 != e1);
std::expected<void, int> e3;
VERIFY(e3 == e3);
VERIFY(e3 != std::unexpected<int>(1));
+ std::expected<const void, long> e4;
+ VERIFY(e3 == e4);
+ VERIFY(e4 == e3);
+
e3 = std::unexpected<int>(1);
VERIFY(e3 == e3);
VERIFY(e3 == std::unexpected<int>(1));
VERIFY(e3 != std::unexpected<int>(2));
+ VERIFY(e3 != e4);
+ VERIFY(e4 != e3);
+
+ e4 = e3;
+ VERIFY(e3 == e4);
+ VERIFY(e4 == e3);
+
+ e4 = std::unexpected<int>(4);
+ VERIFY(e3 != e4);
+ VERIFY(e4 != e3);
return true;
}
--- /dev/null
+// { dg-do run { target c++23 } }
+
+// LWG 4366. Heterogeneous comparison of expected may be ill-formed
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+struct Bool
+{
+ operator bool() const { return true; }
+ explicit operator bool() { throw; }
+};
+
+struct E1 {
+ friend Bool operator==(E1, E1) { return {}; }
+} e1;
+
+struct E2 {
+ friend Bool operator==(E1, E2) { return {}; }
+} e2;
+
+int main()
+{
+ std::expected<int, E1> u1(std::unexpect, e1);
+ VERIFY(u1 == u1);
+
+ std::unexpected<E2> u2(e2);
+ VERIFY(u1 == u2);
+
+ std::expected<void, E1> u3(std::unexpect, e1);
+ VERIFY(u3 == u3);
+ VERIFY(u3 == u2);
+}