-
Notifications
You must be signed in to change notification settings - Fork 13.8k
[libc++] P2944R3: Constrained comparisions - optional
#144249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[libc++] P2944R3: Constrained comparisions - optional
#144249
Conversation
…rence_wrapper` Partially implements [P2944R3](https://wg21.link/P2944R3), which adds constrained comparisons to `optional` and `reference_wrapper`. Generalizes the test helpers in `test_comparisons.h` to support constrained comparisons. - [refwrap.comparisons](https://wg21.link/refwrap.comparisons) - [optional.relops](https://wg21.link/optional.relops) - [https://wg21.link/optional.comp.with.t](https://wg21.link/optional.comp.with.t) Closes llvm#136767 Closes llvm#138233 Relates to: llvm#135759
opational
optional
@llvm/pr-subscribers-libcxx Author: Hristo Hristov (H-G-Hristov) ChangesPartially implements P2944R3 which adds constrained comparisons to std::optional. Closes #136767 Referencesoptional.relops Patch is 31.41 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144249.diff 15 Files Affected:
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 8a0417e120d75..77d09bebe3462 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -59,7 +59,7 @@
"`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","",""
"`P2810R4 <https://wg21.link/P2810R4>`__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","",""
"`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","",""
-"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","The changes to ``optional``, ``tuple`` and ``variant`` are not yet implemented"
+"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","The changes to ``tuple`` and ``variant`` are not yet implemented"
"`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","",""
"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19",""
"","","","","",""
diff --git a/libcxx/include/optional b/libcxx/include/optional
index fa32d75ef86dd..68053bdfb5df3 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -205,6 +205,7 @@ namespace std {
# include <__type_traits/is_assignable.h>
# include <__type_traits/is_constructible.h>
# include <__type_traits/is_convertible.h>
+# include <__type_traits/is_core_convertible.h>
# include <__type_traits/is_destructible.h>
# include <__type_traits/is_nothrow_assignable.h>
# include <__type_traits/is_nothrow_constructible.h>
@@ -982,12 +983,23 @@ public:
template <class _Tp>
optional(_Tp) -> optional<_Tp>;
-// Comparisons between optionals
+// [optional.relops] Relational operators
+
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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 optional<_Up>& __y) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y)
+# if _LIBCPP_STD_VER >= 26
+ requires requires {
+ { *__x == *__y } -> __core_convertible_to<bool>;
+ }
+# endif
+{
if (static_cast<bool>(__x) != static_cast<bool>(__y))
return false;
if (!static_cast<bool>(__x))
@@ -995,11 +1007,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const
return *__x == *__y;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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 optional<_Up>& __y) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y)
+# if _LIBCPP_STD_VER >= 26
+ requires requires {
+ { *__x != *__y } -> __core_convertible_to<bool>;
+ }
+# endif
+{
if (static_cast<bool>(__x) != static_cast<bool>(__y))
return true;
if (!static_cast<bool>(__x))
@@ -1007,11 +1029,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const
return *__x != *__y;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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 optional<_Up>& __y) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y)
+# if _LIBCPP_STD_VER >= 26
+ requires requires {
+ { *__x < *__y } -> __core_convertible_to<bool>;
+ }
+# endif
+{
if (!static_cast<bool>(__y))
return false;
if (!static_cast<bool>(__x))
@@ -1019,11 +1051,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const o
return *__x < *__y;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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 optional<_Up>& __y) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y)
+# if _LIBCPP_STD_VER >= 26
+ requires requires {
+ { *__x > *__y } -> __core_convertible_to<bool>;
+ }
+# endif
+{
if (!static_cast<bool>(__x))
return false;
if (!static_cast<bool>(__y))
@@ -1031,11 +1073,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const o
return *__x > *__y;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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 optional<_Up>& __y) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y)
+# if _LIBCPP_STD_VER >= 26
+ requires requires {
+ { *__x <= *__y } -> __core_convertible_to<bool>;
+ }
+# endif
+{
if (!static_cast<bool>(__x))
return true;
if (!static_cast<bool>(__y))
@@ -1043,11 +1095,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const
return *__x <= *__y;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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 optional<_Up>& __y) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y)
+# if _LIBCPP_STD_VER >= 26
+ requires requires {
+ { *__x >= *__y } -> __core_convertible_to<bool>;
+ }
+# endif
+{
if (!static_cast<bool>(__y))
return true;
if (!static_cast<bool>(__x))
@@ -1067,7 +1129,8 @@ operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
# endif // _LIBCPP_STD_VER >= 20
-// Comparisons with nullopt
+// [optional.nullops] Comparison with nullopt
+
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept {
return !static_cast<bool>(__x);
@@ -1139,100 +1202,221 @@ _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>&
# endif // _LIBCPP_STD_VER <= 17
-// Comparisons with T
+// [optional.comp.with.t] Comparison with T
+
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Up>::value) && requires {
+ { *__x == __v } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? *__x == __v : false;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Tp>::value) && requires {
+ { __v == *__x } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? __v == *__x : false;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Up>::value) && requires {
+ { *__x != __v } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? *__x != __v : true;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Tp>::value) && requires {
+ { __v != *__x } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? __v != *__x : true;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Up>::value) && requires {
+ { *__x < __v } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? *__x < __v : true;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Tp>::value) && requires {
+ { __v < *__x } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? __v < *__x : false;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Up>::value) && requires {
+ { *__x <= __v } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? *__x <= __v : true;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Tp>::value) && requires {
+ { __v <= *__x } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? __v <= *__x : false;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Up>::value) && requires {
+ { *__x > __v } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? *__x > __v : false;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Tp>::value) && requires {
+ { __v > *__x } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? __v > *__x : true;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Up>::value) && requires {
+ { *__x >= __v } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? *__x >= __v : false;
}
+# if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+# else
template <
class _Tp,
class _Up,
enable_if_t<is_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) {
+# endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x)
+# if _LIBCPP_STD_VER >= 26
+ requires(!__is_std_optional<_Tp>::value) && requires {
+ { __v >= *__x } -> __core_convertible_to<bool>;
+ }
+# endif
+{
return static_cast<bool>(__x) ? __v >= *__x : true;
}
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 0636da7bcdf23..54965b270dcce 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
@@ -14,8 +14,29 @@
#include <optional>
+#include "test_comparisons.h"
#include "test_macros.h"
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+
+static_assert(HasOperatorEqual<int, std::optional<int>>);
+static_assert(HasOperatorEqual<int, std::optional<EqualityComparable>>);
+static_assert(HasOperatorEqual<EqualityComparable, std::optional<EqualityComparable>>);
+
+static_assert(!HasOperatorEqual<NonComparable, std::optional<NonComparable>>);
+static_assert(!HasOperatorEqual<NonComparable, std::optional<EqualityComparable>>);
+
+static_assert(HasOperatorEqual<std::optional<int>, int>);
+static_assert(HasOperatorEqual<std::optional<EqualityComparable>, int>);
+static_assert(HasOperatorEqual<std::optional<EqualityComparable>, EqualityComparable>);
+
+static_assert(!HasOperatorEqual<std::optional<NonComparable>, NonComparable>);
+static_assert(!HasOperatorEqual<std::optional<EqualityComparable>, NonComparable>);
+
+#endif
+
using std::optional;
struct X {
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 2010157a8d011..b63b85b000930 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
@@ -14,8 +14,28 @@
#include <optional>
+#include "test_comparisons.h"
#include "test_macros.h"
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+static_assert(HasOperatorGreaterThan<std::optional<ThreeWayComparable>, int>);
+static_assert(HasOperatorGreaterThan<std::optional<ThreeWayComparable>, ThreeWayComparable>);
+
+static_assert(!HasOperatorGreaterThan<std::optional<NonComparable>, NonComparable>);
+static_assert(!HasOperatorGreaterThan<std::optional<ThreeWayComparable>, NonComparable>);
+static_assert(!HasOperatorGreaterThan<std::optional<NonComparable>, ThreeWayComparable>);
+
+static_assert(HasOperatorGreaterThan<int, std::optional<ThreeWayComparable>>);
+static_assert(HasOperatorGreaterThan<ThreeWayComparable, std::optional<ThreeWayComparable>>);
+
+static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<NonComparable>>);
+static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<ThreeWayComparable>>);
+static_assert(!HasOperatorGreaterThan<ThreeWayComparable, std::optional<NonComparable>>);
+
+#endif
+
using std::optional;
struct X {
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 2e1e9b9316111..bb1a8c551d3d5 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
@@ -14,8 +14,28 @@
#include <optional>
+#include "test_comparisons.h"
#include "test_macros.h"
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+static_assert(HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, int>);
+static_assert(HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, ThreeWayComparable>);
+
+static_assert(!HasOperatorGreaterThanEqual<std::optional<NonComparable>, NonComparable>);
+static_assert(!HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, NonComparable>);
+static_assert(!HasOperatorGreaterThanEqual<std::optional<NonComparable>, ThreeWayComparable>);
+
+static_assert(HasOperatorGreaterThanEqual<int, std::optional<ThreeWayComparable>>);
+static_assert(HasOperatorGreaterThanEqual<ThreeWayComparable, std::optional<ThreeWayComparable>>);
+
+static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<NonComparable>>);
+static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<ThreeWayComparable>>);
+static_assert(!HasOperatorGreaterThanEqual<ThreeWayComparable, std::optional<NonComparable>>);
+
+#endif
+
using std::optional;
struct X {
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 7026c24de5dbf..8b08ec3deea5a 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
@@ -14,8 +14,28 @@
#include <optional>
+#include "test_comparisons.h"
#include "test_macros.h"
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+static_assert(HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, int>);
+static_assert(HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, ThreeWayComparable>);
+
+static_assert(!HasOperatorLessThanEqual<std::optional<NonComparable>, NonComparable>);
+static_assert(!HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, NonComparable>);
+static_assert(!HasOperatorLessThanEqual<std::optional<NonComparable>, ThreeWayComparable>);
+
+static_assert(HasOperatorLessThanEqual<int, std::optional<ThreeWayComparable>>);
+static_assert(HasOperatorLessThanEqual<ThreeWayComparable, std::optional<ThreeWayComparable>...
[truncated]
|
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) | ||
# if _LIBCPP_STD_VER >= 26 | ||
requires requires { | ||
{ *__x == *__y } -> __core_convertible_to<bool>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we either have a bug in the old constraint or the new. The paper simply changes Mandates
to Constraints
. That shouldn't change how the constraint looks. I'd also strongly encourage to only have a single constraint style and not different ones. That should significantly reduce the diff here. I think in the end we should simply have a single constraint for all language modes, since the wording didn't fundamentally change.
Partially implements P2944R3 which adds constrained comparisons to std::optional.
Closes #136767
References
optional.relops
optional.comp.with.t