[libcxx-commits] [libcxx] 98baf03 - [libc++] Implement LWG4072: std::optional comparisons: constrain harder (#209968)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Sep 6 20:35:05 PDT 2026
Author: Dan Shan
Date: 2026-09-07T11:34:59+08:00
New Revision: 98baf03a3ca629fa2d690286fb751a05c9fd6965
URL: https://github.com/llvm/llvm-project/commit/98baf03a3ca629fa2d690286fb751a05c9fd6965
DIFF: https://github.com/llvm/llvm-project/commit/98baf03a3ca629fa2d690286fb751a05c9fd6965.diff
LOG: [libc++] Implement LWG4072: std::optional comparisons: constrain harder (#209968)
Resolves #118345.
Previously, the heterogeneous comparison operators (`==`, `!=`, `<`,
`<=`, `>`, `>=`) for arguments `T` and `U` don't check that `T` or `U`
are not a `std::optional` themselves. This allowed the operators to
cause ambiguous overload resolution instead of falling back to
`optional`'s own operators, which possibly caused hard error when there
should not be a matched overload.
This patch implements the resolution by adding
`!__is_std_optional_v<_Up>` (and the `_Tp` equivalent for the reversed)
to the constraints in all twelve involved operators.
As required by the LLVM Project's AI use policy:
- The fix and test processes were revised and verified with AI
assistance.
Added:
Modified:
libcxx/docs/Status/Cxx26Issues.csv
libcxx/include/__optional/comparison.h
libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp
libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index cf30ba8a45ca5..5c5fba32de855 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>`__",""
diff --git a/libcxx/include/__optional/comparison.h b/libcxx/include/__optional/comparison.h
index ba5cddb1acbfb..20e7c68632ce2 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 54965b270dcce..99813dae89031 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>>);
@@ -35,6 +54,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(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
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 b63b85b000930..a945cf4b54002 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>);
@@ -34,6 +53,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(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
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 bb1a8c551d3d5..8e18568bfe6d7 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>);
@@ -34,6 +53,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(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
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 8b08ec3deea5a..c937d7b2a5faf 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>);
@@ -34,6 +53,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(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
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 3ef23a3224520..9a1aa0c65f272 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>);
@@ -34,6 +53,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(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
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 97fb8ee447393..e7940b9b421a3 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>>);
@@ -35,6 +54,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(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
using std::optional;
More information about the libcxx-commits
mailing list