[libcxx-commits] [libcxx] [libc++] Implement LWG4072: std::optional comparisons: constrain harder (PR #209968)
Dan Shan via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Sep 6 18:11:54 PDT 2026
https://github.com/HumanThe2nd updated https://github.com/llvm/llvm-project/pull/209968
>From 53a75b04346f49ad628dcd67956fe6045258c6f7 Mon Sep 17 00:00:00 2001
From: Dan <3shandan at gmail.com>
Date: Sun, 6 Sep 2026 20:59:14 -0400
Subject: [PATCH 1/3] [libc++][optional] Constrain heterogeneous comparison
operators for nested optional
---
libcxx/include/__optional/comparison.h | 65 ++++++++++++-------
.../optional.comp_with_t/equal.pass.cpp | 4 ++
.../optional.comp_with_t/greater.pass.cpp | 4 ++
.../greater_equal.pass.cpp | 4 ++
.../optional.comp_with_t/less_equal.pass.cpp | 4 ++
.../optional.comp_with_t/less_than.pass.cpp | 4 ++
.../optional.comp_with_t/not_equal.pass.cpp | 4 ++
7 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/libcxx/include/__optional/comparison.h b/libcxx/include/__optional/comparison.h
index ba5cddb1acbfb6..20e7c68632ce28 100644
--- a/libcxx/include/__optional/comparison.h
+++ b/libcxx/include/__optional/comparison.h
@@ -13,6 +13,7 @@
#include <__compare/ordering.h>
#include <__compare/three_way_comparable.h>
#include <__config>
+#include <__optional/common.h>
#include <__optional/nullopt_t.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_constructible.h>
@@ -201,7 +202,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>&
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+ enable_if_t<!__is_std_optional_v<_Up> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -212,7 +214,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+ enable_if_t<!__is_std_optional_v<_Tp> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
@@ -223,7 +226,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+ enable_if_t<!__is_std_optional_v<_Up> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -234,7 +238,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+ enable_if_t<!__is_std_optional_v<_Tp> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
@@ -242,20 +247,24 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_
return true;
}
-template < class _Tp,
- class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
- int> = 0>
+template <
+ class _Tp,
+ class _Up,
+ enable_if_t<!__is_std_optional_v<_Up> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+ int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
return *__x < __v;
return true;
}
-template < class _Tp,
- class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
- int> = 0>
+template <
+ class _Tp,
+ class _Up,
+ enable_if_t<!__is_std_optional_v<_Tp> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+ int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
return __v < *__x;
@@ -265,7 +274,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_U
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+ enable_if_t<!__is_std_optional_v<_Up> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -276,7 +286,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+ enable_if_t<!__is_std_optional_v<_Tp> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
@@ -284,20 +295,24 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_
return false;
}
-template < class _Tp,
- class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
- int> = 0>
+template <
+ class _Tp,
+ class _Up,
+ enable_if_t<!__is_std_optional_v<_Up> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+ int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
return *__x > __v;
return false;
}
-template < class _Tp,
- class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
- int> = 0>
+template <
+ class _Tp,
+ class _Up,
+ enable_if_t<!__is_std_optional_v<_Tp> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+ int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
return __v > *__x;
@@ -307,7 +322,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_U
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+ enable_if_t<!__is_std_optional_v<_Up> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -318,7 +334,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+ enable_if_t<!__is_std_optional_v<_Tp> &&
+ __is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
index 54965b270dcce0..831027148ac58e 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
@@ -35,6 +35,10 @@ static_assert(HasOperatorEqual<std::optional<EqualityComparable>, EqualityCompar
static_assert(!HasOperatorEqual<std::optional<NonComparable>, NonComparable>);
static_assert(!HasOperatorEqual<std::optional<EqualityComparable>, NonComparable>);
+// LWG4072: avoid ambiguity with optional's own comparison operators
+static_assert(!HasOperatorEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorEqual<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
index b63b85b0009301..6af588d171d05a 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
@@ -34,6 +34,10 @@ static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<NonComparable
static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<ThreeWayComparable>>);
static_assert(!HasOperatorGreaterThan<ThreeWayComparable, std::optional<NonComparable>>);
+// LWG4072: avoid ambiguity with optional's own comparison operators
+static_assert(!HasOperatorGreaterThan<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorGreaterThan<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
index bb1a8c551d3d5c..3ae7f4536e0ae2 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
@@ -34,6 +34,10 @@ static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<NonCompa
static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<ThreeWayComparable>>);
static_assert(!HasOperatorGreaterThanEqual<ThreeWayComparable, std::optional<NonComparable>>);
+// LWG4072: avoid ambiguity with optional's own comparison operators
+static_assert(!HasOperatorGreaterThanEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorGreaterThanEqual<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
index 8b08ec3deea5a0..43313e679b1f77 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
@@ -34,6 +34,10 @@ static_assert(!HasOperatorLessThanEqual<NonComparable, std::optional<NonComparab
static_assert(!HasOperatorLessThanEqual<NonComparable, std::optional<ThreeWayComparable>>);
static_assert(!HasOperatorLessThanEqual<ThreeWayComparable, std::optional<NonComparable>>);
+// LWG4072: avoid ambiguity with optional's own comparison operators
+static_assert(!HasOperatorLessThanEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorLessThanEqual<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp
index 3ef23a32245209..6f3ccf4db8bf47 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp
@@ -34,6 +34,10 @@ static_assert(!HasOperatorLessThan<NonComparable, std::optional<NonComparable>>)
static_assert(!HasOperatorLessThan<NonComparable, std::optional<ThreeWayComparable>>);
static_assert(!HasOperatorLessThan<ThreeWayComparable, std::optional<NonComparable>>);
+// LWG4072: avoid ambiguity with optional's own comparison operators
+static_assert(!HasOperatorLessThan<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorLessThan<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp
index 97fb8ee4473936..74f1658625d7df 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp
@@ -35,6 +35,10 @@ static_assert(HasOperatorNotEqual<std::optional<EqualityComparable>, EqualityCom
static_assert(!HasOperatorNotEqual<std::optional<NonComparable>, NonComparable>);
static_assert(!HasOperatorNotEqual<std::optional<EqualityComparable>, NonComparable>);
+// LWG4072: avoid ambiguity with optional's own comparison operators
+static_assert(!HasOperatorNotEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorNotEqual<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
>From 15af8d4c4f57061b09e037659f6c1a8408c0305b Mon Sep 17 00:00:00 2001
From: Dan <3shandan at gmail.com>
Date: Sun, 6 Sep 2026 20:59:15 -0400
Subject: [PATCH 2/3] Mark LWG4072 as complete in Cxx26Issues.csv
---
libcxx/docs/Status/Cxx26Issues.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index cf30ba8a45ca5d..5c5fba32de8556 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -87,7 +87,7 @@
"`LWG4027 <https://wg21.link/LWG4027>`__","``possibly-const-range`` should prefer returning ``const R&``","2024-11 (Wrocław)","","","`#118342 <https://github.com/llvm/llvm-project/issues/118342>`__",""
"`LWG4044 <https://wg21.link/LWG4044>`__","Confusing requirements for ``std::print`` on POSIX platforms","2024-11 (Wrocław)","","","`#118343 <https://github.com/llvm/llvm-project/issues/118343>`__",""
"`LWG4064 <https://wg21.link/LWG4064>`__","Clarify that ``std::launder`` is not needed when using the result of ``std::memcpy``","2024-11 (Wrocław)","","","`#118344 <https://github.com/llvm/llvm-project/issues/118344>`__",""
-"`LWG4072 <https://wg21.link/LWG4072>`__","``std::optional`` comparisons: constrain harder","2024-11 (Wrocław)","","","`#118345 <https://github.com/llvm/llvm-project/issues/118345>`__",""
+"`LWG4072 <https://wg21.link/LWG4072>`__","``std::optional`` comparisons: constrain harder","2024-11 (Wrocław)","|Complete|","24","`#118345 <https://github.com/llvm/llvm-project/issues/118345>`__",""
"`LWG4084 <https://wg21.link/LWG4084>`__","``std::fixed`` ignores ``std::uppercase``","2024-11 (Wrocław)","|Complete|","","`#118346 <https://github.com/llvm/llvm-project/issues/118346>`__",""
"`LWG4085 <https://wg21.link/LWG4085>`__","``ranges::generate_random``'s helper lambda should specify the return type","2024-11 (Wrocław)","","","`#118347 <https://github.com/llvm/llvm-project/issues/118347>`__",""
"`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
>From f6b7a55692bf1882d91f1529c3f405649c557ad6 Mon Sep 17 00:00:00 2001
From: Dan <3shandan at gmail.com>
Date: Sun, 6 Sep 2026 21:11:36 -0400
Subject: [PATCH 3/3] restore accidental rollback
---
.../optional.comp_with_t/equal.pass.cpp | 23 +++++++++++++++++--
.../optional.comp_with_t/greater.pass.cpp | 23 +++++++++++++++++--
.../greater_equal.pass.cpp | 23 +++++++++++++++++--
.../optional.comp_with_t/less_equal.pass.cpp | 23 +++++++++++++++++--
.../optional.comp_with_t/less_than.pass.cpp | 23 +++++++++++++++++--
.../optional.comp_with_t/not_equal.pass.cpp | 23 +++++++++++++++++--
6 files changed, 126 insertions(+), 12 deletions(-)
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
index 831027148ac58e..99813dae89031b 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
@@ -19,6 +19,25 @@
#if TEST_STD_VER >= 26
+struct tester {};
+
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator==(const tester& a, const T& t) {
+ return t.compare(a) == 0;
+}
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator==(const T& t, const tester& a) {
+ return t.compare(a) == 0;
+}
+
+template <class T, class U>
+constexpr std::optional<bool> try_cmp_eq(const T& t, const U& u) {
+ if constexpr (requires { t == u; })
+ return t == u;
+ else
+ return {};
+}
+
// Test SFINAE.
static_assert(HasOperatorEqual<int, std::optional<int>>);
@@ -36,8 +55,8 @@ static_assert(!HasOperatorEqual<std::optional<NonComparable>, NonComparable>);
static_assert(!HasOperatorEqual<std::optional<EqualityComparable>, NonComparable>);
// LWG4072: avoid ambiguity with optional's own comparison operators
-static_assert(!HasOperatorEqual<std::optional<void*>, std::optional<int>>);
-static_assert(!HasOperatorEqual<std::optional<int>, std::optional<void*>>);
+static_assert(try_cmp_eq(std::optional<int>{}, std::optional<tester>{}) == std::optional<bool>{});
+static_assert(try_cmp_eq(std::optional<int>{}, std::optional<int>{}) == std::optional<bool>{true});
#endif
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
index 6af588d171d05a..a945cf4b540024 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
@@ -19,6 +19,25 @@
#if TEST_STD_VER >= 26
+struct tester {};
+
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator>(const tester& a, const T& t) {
+ return t.compare(a) < 0;
+}
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator>(const T& t, const tester& a) {
+ return t.compare(a) > 0;
+}
+
+template <class T, class U>
+constexpr std::optional<bool> try_cmp_gt(const T& t, const U& u) {
+ if constexpr (requires { t > u; })
+ return t > u;
+ else
+ return {};
+}
+
// Test SFINAE.
static_assert(HasOperatorGreaterThan<std::optional<ThreeWayComparable>, int>);
static_assert(HasOperatorGreaterThan<std::optional<ThreeWayComparable>, ThreeWayComparable>);
@@ -35,8 +54,8 @@ static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<ThreeWayCompa
static_assert(!HasOperatorGreaterThan<ThreeWayComparable, std::optional<NonComparable>>);
// LWG4072: avoid ambiguity with optional's own comparison operators
-static_assert(!HasOperatorGreaterThan<std::optional<void*>, std::optional<int>>);
-static_assert(!HasOperatorGreaterThan<std::optional<int>, std::optional<void*>>);
+static_assert(try_cmp_gt(std::optional<int>{}, std::optional<tester>{}) == std::optional<bool>{});
+static_assert(try_cmp_gt(std::optional<int>{}, std::optional<int>{}) == std::optional<bool>{false});
#endif
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
index 3ae7f4536e0ae2..8e18568bfe6d7a 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
@@ -19,6 +19,25 @@
#if TEST_STD_VER >= 26
+struct tester {};
+
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator>=(const tester& a, const T& t) {
+ return t.compare(a) <= 0;
+}
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator>=(const T& t, const tester& a) {
+ return t.compare(a) >= 0;
+}
+
+template <class T, class U>
+constexpr std::optional<bool> try_cmp_gteq(const T& t, const U& u) {
+ if constexpr (requires { t >= u; })
+ return t >= u;
+ else
+ return {};
+}
+
// Test SFINAE.
static_assert(HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, int>);
static_assert(HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, ThreeWayComparable>);
@@ -35,8 +54,8 @@ static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<ThreeWay
static_assert(!HasOperatorGreaterThanEqual<ThreeWayComparable, std::optional<NonComparable>>);
// LWG4072: avoid ambiguity with optional's own comparison operators
-static_assert(!HasOperatorGreaterThanEqual<std::optional<void*>, std::optional<int>>);
-static_assert(!HasOperatorGreaterThanEqual<std::optional<int>, std::optional<void*>>);
+static_assert(try_cmp_gteq(std::optional<int>{}, std::optional<tester>{}) == std::optional<bool>{});
+static_assert(try_cmp_gteq(std::optional<int>{}, std::optional<int>{}) == std::optional<bool>{true});
#endif
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
index 43313e679b1f77..c937d7b2a5faf0 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
@@ -19,6 +19,25 @@
#if TEST_STD_VER >= 26
+struct tester {};
+
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator<=(const tester& a, const T& t) {
+ return t.compare(a) >= 0;
+}
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator<=(const T& t, const tester& a) {
+ return t.compare(a) <= 0;
+}
+
+template <class T, class U>
+constexpr std::optional<bool> try_cmp_lteq(const T& t, const U& u) {
+ if constexpr (requires { t <= u; })
+ return t <= u;
+ else
+ return {};
+}
+
// Test SFINAE.
static_assert(HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, int>);
static_assert(HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, ThreeWayComparable>);
@@ -35,8 +54,8 @@ static_assert(!HasOperatorLessThanEqual<NonComparable, std::optional<ThreeWayCom
static_assert(!HasOperatorLessThanEqual<ThreeWayComparable, std::optional<NonComparable>>);
// LWG4072: avoid ambiguity with optional's own comparison operators
-static_assert(!HasOperatorLessThanEqual<std::optional<void*>, std::optional<int>>);
-static_assert(!HasOperatorLessThanEqual<std::optional<int>, std::optional<void*>>);
+static_assert(try_cmp_lteq(std::optional<int>{}, std::optional<tester>{}) == std::optional<bool>{});
+static_assert(try_cmp_lteq(std::optional<int>{}, std::optional<int>{}) == std::optional<bool>{true});
#endif
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp
index 6f3ccf4db8bf47..9a1aa0c65f272e 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp
@@ -19,6 +19,25 @@
#if TEST_STD_VER >= 26
+struct tester {};
+
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator<(const tester& a, const T& t) {
+ return t.compare(a) > 0;
+}
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator<(const T& t, const tester& a) {
+ return t.compare(a) < 0;
+}
+
+template <class T, class U>
+constexpr std::optional<bool> try_cmp_lt(const T& t, const U& u) {
+ if constexpr (requires { t < u; })
+ return t < u;
+ else
+ return {};
+}
+
// Test SFINAE.
static_assert(HasOperatorLessThan<std::optional<ThreeWayComparable>, int>);
static_assert(HasOperatorLessThan<std::optional<ThreeWayComparable>, ThreeWayComparable>);
@@ -35,8 +54,8 @@ static_assert(!HasOperatorLessThan<NonComparable, std::optional<ThreeWayComparab
static_assert(!HasOperatorLessThan<ThreeWayComparable, std::optional<NonComparable>>);
// LWG4072: avoid ambiguity with optional's own comparison operators
-static_assert(!HasOperatorLessThan<std::optional<void*>, std::optional<int>>);
-static_assert(!HasOperatorLessThan<std::optional<int>, std::optional<void*>>);
+static_assert(try_cmp_lt(std::optional<int>{}, std::optional<tester>{}) == std::optional<bool>{});
+static_assert(try_cmp_lt(std::optional<int>{}, std::optional<int>{}) == std::optional<bool>{false});
#endif
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp
index 74f1658625d7df..e7940b9b421a3b 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp
@@ -19,6 +19,25 @@
#if TEST_STD_VER >= 26
+struct tester {};
+
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator!=(const tester& a, const T& t) {
+ return t.compare(a) != 0;
+}
+template <class T, std::enable_if_t<std::is_class_v<T>, int> = 0> // intentionally underconstrained
+constexpr bool operator!=(const T& t, const tester& a) {
+ return t.compare(a) != 0;
+}
+
+template <class T, class U>
+constexpr std::optional<bool> try_cmp_neq(const T& t, const U& u) {
+ if constexpr (requires { t != u; })
+ return t != u;
+ else
+ return {};
+}
+
// Test SFINAE.
static_assert(HasOperatorNotEqual<int, std::optional<int>>);
@@ -36,8 +55,8 @@ static_assert(!HasOperatorNotEqual<std::optional<NonComparable>, NonComparable>)
static_assert(!HasOperatorNotEqual<std::optional<EqualityComparable>, NonComparable>);
// LWG4072: avoid ambiguity with optional's own comparison operators
-static_assert(!HasOperatorNotEqual<std::optional<void*>, std::optional<int>>);
-static_assert(!HasOperatorNotEqual<std::optional<int>, std::optional<void*>>);
+static_assert(try_cmp_neq(std::optional<int>{}, std::optional<tester>{}) == std::optional<bool>{});
+static_assert(try_cmp_neq(std::optional<int>{}, std::optional<int>{}) == std::optional<bool>{false});
#endif
More information about the libcxx-commits
mailing list