* [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386]
@ 2024-04-10 8:45 Jonathan Wakely
2024-04-10 8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10 8:45 UTC (permalink / raw)
To: libstdc++, gcc-patches
Tested x86_64-linux.
Since this only affects C++20 and later it seems OK for trunk now.
-- >8 --
I'm only treating this as a DR for C++20 for now, because it's less work
and only requires changes to operator== and operator<=>. To do this for
older standards would require changes to the six relational operators
used pre-C++20.
libstdc++-v3/ChangeLog:
PR libstdc++/113386
* include/bits/stl_pair.h (operator==, operator<=>): Support
heterogeneous comparisons, as per LWG 3865.
* testsuite/20_util/pair/comparison_operators/lwg3865.cc: New
test.
---
libstdc++-v3/include/bits/stl_pair.h | 32 ++++++++++++++-----
.../pair/comparison_operators/lwg3865.cc | 15 +++++++++
2 files changed, 39 insertions(+), 8 deletions(-)
create mode 100644 libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 4f5c8389fa6..45317417c9c 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -1000,23 +1000,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _T1, typename _T2> pair(_T1, _T2) -> pair<_T1, _T2>;
#endif
- /// Two pairs of the same type are equal iff their members are equal.
- template<typename _T1, typename _T2>
+#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 3865. Sorting a range of pairs
+
+ /// Two pairs are equal iff their members are equal.
+ template<typename _T1, typename _T2, typename _U1, typename _U2>
inline _GLIBCXX_CONSTEXPR bool
- operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+ operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
{ return __x.first == __y.first && __x.second == __y.second; }
-#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
- template<typename _T1, typename _T2>
- constexpr common_comparison_category_t<__detail::__synth3way_t<_T1>,
- __detail::__synth3way_t<_T2>>
- operator<=>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+ /** Defines a lexicographical order for pairs.
+ *
+ * For two pairs of comparable types, `P` is ordered before `Q` if
+ * `P.first` is less than `Q.first`, or if `P.first` and `Q.first`
+ * are equivalent (neither is less than the other) and `P.second` is
+ * less than `Q.second`.
+ */
+ template<typename _T1, typename _T2, typename _U1, typename _U2>
+ constexpr common_comparison_category_t<__detail::__synth3way_t<_T1, _U1>,
+ __detail::__synth3way_t<_T2, _U2>>
+ operator<=>(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
{
if (auto __c = __detail::__synth3way(__x.first, __y.first); __c != 0)
return __c;
return __detail::__synth3way(__x.second, __y.second);
}
#else
+ /// Two pairs of the same type are equal iff their members are equal.
+ template<typename _T1, typename _T2>
+ inline _GLIBCXX_CONSTEXPR bool
+ operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+ { return __x.first == __y.first && __x.second == __y.second; }
+
/** Defines a lexicographical order for pairs.
*
* For two pairs of the same type, `P` is ordered before `Q` if
diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
new file mode 100644
index 00000000000..2bbd54af192
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
@@ -0,0 +1,15 @@
+// { dg-do run { target c++20 } }
+
+// LWG 3865. Sorting a range of pairs
+
+#include <utility>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ std::pair<int, int> p(1, 2);
+ std::pair<int&, int&> p2(p.first, p.second);
+ VERIFY( p == p2 );
+ VERIFY( p <= p2 );
+ VERIFY( p >= p2 );
+}
--
2.44.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26
2024-04-10 8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
@ 2024-04-10 8:45 ` Jonathan Wakely
2024-04-15 18:30 ` Jonathan Wakely
2024-04-10 8:45 ` [PATCH 3/4] libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant Jonathan Wakely
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10 8:45 UTC (permalink / raw)
To: libstdc++, gcc-patches
Tested x86_64-linux.
Since this only affects C++26 it seems OK for trunk now.
-- >8 --
This C++26 change was just approved in Tokyo, in P2944R3. It adds
operator== and operator<=> overloads to std::reference_wrapper.
The operator<=> overloads in the paper cause compilation errors for any
type without <=> so they're implemented here with deduced return types
and constrained by a requires clause.
libstdc++-v3/ChangeLog:
* include/bits/refwrap.h (reference_wrapper): Add comparison
operators as proposed by P2944R3.
* include/bits/version.def (reference_wrapper): Define.
* include/bits/version.h: Regenerate.
* include/std/functional: Enable feature test macro.
* testsuite/20_util/reference_wrapper/compare.cc: New test.
---
libstdc++-v3/include/bits/refwrap.h | 45 +++++++++
libstdc++-v3/include/bits/version.def | 8 ++
libstdc++-v3/include/bits/version.h | 10 ++
libstdc++-v3/include/std/functional | 1 +
.../20_util/reference_wrapper/compare.cc | 95 +++++++++++++++++++
5 files changed, 159 insertions(+)
create mode 100644 libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
diff --git a/libstdc++-v3/include/bits/refwrap.h b/libstdc++-v3/include/bits/refwrap.h
index 2d4338b718f..fd1cc2b63e6 100644
--- a/libstdc++-v3/include/bits/refwrap.h
+++ b/libstdc++-v3/include/bits/refwrap.h
@@ -38,6 +38,10 @@
#include <bits/invoke.h>
#include <bits/stl_function.h> // for unary_function and binary_function
+#if __glibcxx_reference_wrapper >= 202403L // >= C++26
+# include <compare>
+#endif
+
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -358,6 +362,47 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type)
#endif
return std::__invoke(get(), std::forward<_Args>(__args)...);
}
+
+#if __glibcxx_reference_wrapper >= 202403L // >= C++26
+ // [refwrap.comparisons], comparisons
+ [[nodiscard]]
+ friend constexpr bool
+ operator==(reference_wrapper __x, reference_wrapper __y)
+ requires requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
+ { return __x.get() == __y.get(); }
+
+ [[nodiscard]]
+ friend constexpr bool
+ operator==(reference_wrapper __x, const _Tp& __y)
+ requires requires { { __x.get() == __y } -> convertible_to<bool>; }
+ { return __x.get() == __y; }
+
+ [[nodiscard]]
+ friend constexpr bool
+ operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+ requires (!is_const_v<_Tp>)
+ && requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
+ { return __x.get() == __y.get(); }
+
+ [[nodiscard]]
+ friend constexpr auto
+ operator<=>(reference_wrapper __x, reference_wrapper<_Tp> __y)
+ requires requires { __detail::__synth3way(__x.get(), __y.get()); }
+ { return __detail::__synth3way(__x.get(), __y.get()); }
+
+ [[nodiscard]]
+ friend constexpr auto
+ operator<=>(reference_wrapper __x, const _Tp& __y)
+ requires requires { __detail::__synth3way(__x.get(), __y); }
+ { return __detail::__synth3way(__x.get(), __y); }
+
+ [[nodiscard]]
+ friend constexpr auto
+ operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+ requires (!is_const_v<_Tp>)
+ && requires { __detail::__synth3way(__x.get(), __y.get()); }
+ { return __detail::__synth3way(__x.get(), __y.get()); }
+#endif
};
#if __cpp_deduction_guides
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 5ad44941bff..5c0477fb61e 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1760,6 +1760,14 @@ ftms = {
};
};
+ftms = {
+ name = reference_wrapper;
+ values = {
+ v = 202403;
+ cxxmin = 26;
+ };
+};
+
ftms = {
name = saturation_arithmetic;
values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 460a3e0116a..65e708c73fb 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1963,6 +1963,16 @@
#endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
#undef __glibcxx_want_ratio
+#if !defined(__cpp_lib_reference_wrapper)
+# if (__cplusplus > 202302L)
+# define __glibcxx_reference_wrapper 202403L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_reference_wrapper)
+# define __cpp_lib_reference_wrapper 202403L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_reference_wrapper) && defined(__glibcxx_want_reference_wrapper) */
+#undef __glibcxx_want_reference_wrapper
+
#if !defined(__cpp_lib_saturation_arithmetic)
# if (__cplusplus > 202302L)
# define __glibcxx_saturation_arithmetic 202311L
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 766558b3ce0..99364286a72 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -83,6 +83,7 @@
#define __glibcxx_want_move_only_function
#define __glibcxx_want_not_fn
#define __glibcxx_want_ranges
+#define __glibcxx_want_reference_wrapper
#define __glibcxx_want_transparent_operators
#include <bits/version.h>
diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
new file mode 100644
index 00000000000..039c9d26496
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
@@ -0,0 +1,95 @@
+// { dg-do compile { target c++26 } }
+
+
+#include <functional>
+
+#ifndef __cpp_lib_reference_wrapper
+# error "Feature-test macro for reference_wrapper missing"
+#elif __cpp_lib_reference_wrapper != 202403
+# error "Feature-test macro for reference_wrapper has wrong value"
+#endif
+
+// P2944R3 Comparisons for reference_wrapper
+
+auto check(int i, std::reference_wrapper<int> r) -> bool {
+ return i == r;
+}
+
+template <class T> using Ref = std::reference_wrapper<T>;
+
+template <class T>
+concept ref_equality_comparable
+= requires (T a, T const ca, Ref<T> r, Ref<T const> cr) {
+ // the usual T is equality-comparable with itself
+ a == a;
+ a == ca;
+ ca == ca;
+
+ // Ref<T> is equality-comparable with itself
+ r == r;
+ r == cr;
+ cr == cr;
+
+ // T and Ref<T> are equality-comparable
+ a == r;
+ a == cr;
+ ca == r;
+ ca == cr;
+};
+
+static_assert( ref_equality_comparable<int> );
+
+struct A {
+ auto operator==(A const&) const -> bool { return true; }
+};
+
+struct B {
+ friend auto operator==(B const&, B const&) -> bool { return true; }
+};
+
+template <class T>
+struct C {
+ friend auto operator==(C const&, C const&) -> bool { return true; }
+};
+
+template <class T>
+struct D { };
+template <class T>
+auto operator==(D<T> const&, D<T> const&) -> bool { return true; }
+
+static_assert(ref_equality_comparable<int>);
+static_assert(ref_equality_comparable<A>);
+static_assert(ref_equality_comparable<B>);
+static_assert(ref_equality_comparable<C<int>>);
+static_assert(ref_equality_comparable<D<int>>);
+#include <string_view>
+static_assert(ref_equality_comparable<std::string_view>);
+
+template <typename T>
+struct ValArray {
+ friend auto operator==(ValArray const&, ValArray const&) -> ValArray<bool> {
+ return {};
+ }
+};
+
+void f(ValArray<int> v) {
+ // this is valid and has type ValArray<bool>
+ v == v;
+
+ // this is also valid today and has the same type
+ std::ref(v) == std::ref(v);
+}
+
+struct ComparesAsInt {
+ friend auto operator==(ComparesAsInt, ComparesAsInt) -> int;
+};
+
+auto f(std::reference_wrapper<ComparesAsInt> a,
+ std::reference_wrapper<ComparesAsInt> b) {
+ // today: compiles and returns int
+ // proposed: compiles and returns bool
+ return a == b;
+}
+
+ComparesAsInt& c();
+static_assert( std::is_same_v<decltype(f(c(), c())), bool> );
--
2.44.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/4] libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant
2024-04-10 8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
2024-04-10 8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
@ 2024-04-10 8:45 ` Jonathan Wakely
2024-04-10 8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
2024-04-15 18:29 ` [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
3 siblings, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10 8:45 UTC (permalink / raw)
To: libstdc++, gcc-patches
Tested x86_64-linux.
Since this only affects C++20 and later (except for adding [[nodiscard]]
to relational ops) it seems OK for trunk now.
-- >8 --
Implement the changes from P2944R3 which add constraints to the
comparison operators of std::pair, std::tuple, and std::variant.
The paper also changes std::optional, but we already constrain its
comparisons using SFINAE on the return type. However, we need some
additional constraints on the [optional.comp.with.t] operators that
compare an optional with a value. The paper doesn't say to do that, but
I think it's needed because otherwise when the comparison for two
optional objects fails its constraints, the two overloads that are
supposed to be for comparing to a non-optional become the best overload
candidates, but are ambiguous (and we don't even get as far as checking
the constraints for satisfaction).
The paper does not change std::expected, but probably should have done.
I'll submit an LWG issue about that and implement it separately.
Also add [[nodiscard]] to all these comparison operators.
libstdc++-v3/ChangeLog:
* include/bits/stl_pair.h (operator==): Add constraint.
* include/bits/version.def (constrained_equality): Define.
* include/bits/version.h: Regenerate.
* include/std/optional: Define feature test macro.
(__optional_rep_op_t): Use is_convertible_v instead of
is_convertible.
* include/std/tuple: Define feature test macro.
(operator==, __tuple_cmp, operator<=>): Reimplement C++20
comparisons using lambdas. Add constraints.
* include/std/utility: Define feature test macro.
* include/std/variant: Define feature test macro.
(_VARIANT_RELATION_FUNCTION_TEMPLATE): Add constraints.
(variant): Remove unnecessary friend declarations for comparison
operators.
* testsuite/20_util/optional/relops/constrained.cc: New test.
* testsuite/20_util/pair/comparison_operators/constrained.cc:
New test.
* testsuite/20_util/tuple/comparison_operators/constrained.cc:
New test.
* testsuite/20_util/variant/relops/constrained.cc: New test.
* testsuite/20_util/tuple/comparison_operators/overloaded.cc:
Disable for C++20 and later.
* testsuite/20_util/tuple/comparison_operators/overloaded2.cc:
Remove dg-error line for target c++20.
---
libstdc++-v3/include/bits/stl_pair.h | 16 +-
libstdc++-v3/include/bits/version.def | 9 +
libstdc++-v3/include/bits/version.h | 10 +
libstdc++-v3/include/std/optional | 48 +++-
libstdc++-v3/include/std/tuple | 102 ++++---
libstdc++-v3/include/std/utility | 1 +
libstdc++-v3/include/std/variant | 28 +-
.../20_util/optional/relops/constrained.cc | 258 ++++++++++++++++++
.../pair/comparison_operators/constrained.cc | 48 ++++
.../tuple/comparison_operators/constrained.cc | 50 ++++
.../tuple/comparison_operators/overloaded.cc | 6 +-
.../tuple/comparison_operators/overloaded2.cc | 1 -
.../20_util/variant/relops/constrained.cc | 175 ++++++++++++
13 files changed, 677 insertions(+), 75 deletions(-)
create mode 100644 libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
create mode 100644 libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
create mode 100644 libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
create mode 100644 libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 45317417c9c..0c1e5719a1a 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -1000,14 +1000,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _T1, typename _T2> pair(_T1, _T2) -> pair<_T1, _T2>;
#endif
-#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
+#if __cpp_lib_three_way_comparison
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3865. Sorting a range of pairs
/// Two pairs are equal iff their members are equal.
template<typename _T1, typename _T2, typename _U1, typename _U2>
- inline _GLIBCXX_CONSTEXPR bool
+ [[nodiscard]]
+ constexpr bool
operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
+ requires requires {
+ { __x.first == __y.first } -> __detail::__boolean_testable;
+ { __x.second == __y.second } -> __detail::__boolean_testable;
+ }
{ return __x.first == __y.first && __x.second == __y.second; }
/** Defines a lexicographical order for pairs.
@@ -1018,6 +1023,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* less than `Q.second`.
*/
template<typename _T1, typename _T2, typename _U1, typename _U2>
+ [[nodiscard]]
constexpr common_comparison_category_t<__detail::__synth3way_t<_T1, _U1>,
__detail::__synth3way_t<_T2, _U2>>
operator<=>(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
@@ -1029,6 +1035,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#else
/// Two pairs of the same type are equal iff their members are equal.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return __x.first == __y.first && __x.second == __y.second; }
@@ -1041,6 +1048,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* than `Q.second`.
*/
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return __x.first < __y.first
@@ -1048,24 +1056,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// Uses @c operator== to find the result.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator!=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return !(__x == __y); }
/// Uses @c operator< to find the result.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return __y < __x; }
/// Uses @c operator< to find the result.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator<=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return !(__y < __x); }
/// Uses @c operator< to find the result.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator>=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return !(__x < __y); }
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 5c0477fb61e..f0ba4f2bb3d 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1237,6 +1237,15 @@ ftms = {
};
};
+ftms = {
+ name = constrained_equality;
+ values = {
+ v = 202403;
+ cxxmin = 20;
+ extra_cond = "__glibcxx_three_way_comparison";
+ };
+};
+
ftms = {
name = erase_if;
values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 65e708c73fb..f30f51dcedc 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1373,6 +1373,16 @@
#endif /* !defined(__cpp_lib_constexpr_vector) && defined(__glibcxx_want_constexpr_vector) */
#undef __glibcxx_want_constexpr_vector
+#if !defined(__cpp_lib_constrained_equality)
+# if (__cplusplus >= 202002L) && (__glibcxx_three_way_comparison)
+# define __glibcxx_constrained_equality 202403L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constrained_equality)
+# define __cpp_lib_constrained_equality 202403L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_constrained_equality) && defined(__glibcxx_want_constrained_equality) */
+#undef __glibcxx_want_constrained_equality
+
#if !defined(__cpp_lib_erase_if)
# if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED
# define __glibcxx_erase_if 202002L
diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional
index 3507c36a4d8..e245e76e7a0 100644
--- a/libstdc++-v3/include/std/optional
+++ b/libstdc++-v3/include/std/optional
@@ -34,6 +34,7 @@
#define __glibcxx_want_freestanding_optional
#define __glibcxx_want_optional
+#define __glibcxx_want_constrained_equality
#include <bits/version.h>
#ifdef __cpp_lib_optional // C++ >= 17
@@ -1194,7 +1195,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp>
using __optional_relop_t =
- enable_if_t<is_convertible<_Tp, bool>::value, bool>;
+ enable_if_t<is_convertible_v<_Tp, bool>, bool>;
template<typename _Tp, typename _Up>
using __optional_eq_t = __optional_relop_t<
@@ -1279,6 +1280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#ifdef __cpp_lib_three_way_comparison
template<typename _Tp, three_way_comparable_with<_Tp> _Up>
+ [[nodiscard]]
constexpr compare_three_way_result_t<_Tp, _Up>
operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y)
{
@@ -1288,12 +1290,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Comparisons with nullopt.
template<typename _Tp>
+ [[nodiscard]]
constexpr bool
operator==(const optional<_Tp>& __lhs, nullopt_t) noexcept
{ return !__lhs; }
#ifdef __cpp_lib_three_way_comparison
template<typename _Tp>
+ [[nodiscard]]
constexpr strong_ordering
operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept
{ return bool(__x) <=> false; }
@@ -1354,76 +1358,94 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return !__rhs; }
#endif // three-way-comparison
+#if __cpp_lib_concepts
+# define _REQUIRES_NOT_OPTIONAL(T) requires (!__is_optional_v<T>)
+#else
+# define _REQUIRES_NOT_OPTIONAL(T)
+#endif
+
// Comparisons with value type.
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator==(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator== [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_eq_t<_Tp, _Up>
{ return __lhs && *__lhs == __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
operator==(const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_eq_t<_Up, _Tp>
{ return __rhs && __lhs == *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator!=(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator!= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_ne_t<_Tp, _Up>
{ return !__lhs || *__lhs != __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator!=(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator!= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_ne_t<_Up, _Tp>
{ return !__rhs || __lhs != *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator<(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator< [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_lt_t<_Tp, _Up>
{ return !__lhs || *__lhs < __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator<(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator< [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_lt_t<_Up, _Tp>
{ return __rhs && __lhs < *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator>(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator> [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_gt_t<_Tp, _Up>
{ return __lhs && *__lhs > __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator>(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator> [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_gt_t<_Up, _Tp>
{ return !__rhs || __lhs > *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator<=(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator<= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_le_t<_Tp, _Up>
{ return !__lhs || *__lhs <= __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator<=(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator<= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_le_t<_Up, _Tp>
{ return __rhs && __lhs <= *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator>=(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator>= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_ge_t<_Tp, _Up>
{ return __lhs && *__lhs >= __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator>=(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator>= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_ge_t<_Up, _Tp>
{ return !__rhs || __lhs >= *__rhs; }
@@ -1432,7 +1454,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
requires (!__is_optional_v<_Up>)
&& three_way_comparable_with<_Up, _Tp>
constexpr compare_three_way_result_t<_Tp, _Up>
- operator<=>(const optional<_Tp>& __x, const _Up& __v)
+ operator<=> [[nodiscard]] (const optional<_Tp>& __x, const _Up& __v)
{ return bool(__x) ? *__x <=> __v : strong_ordering::less; }
#endif
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 3065058e184..df3f6e38eeb 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -51,6 +51,7 @@
#define __glibcxx_want_make_from_tuple
#define __glibcxx_want_ranges_zip
#define __glibcxx_want_tuple_like
+#define __glibcxx_want_constrained_equality
#include <bits/version.h>
namespace std _GLIBCXX_VISIBILITY(default)
@@ -250,17 +251,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cpp_lib_tuple_like // >= C++23
struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; };
- // These forward declarations are used by the operator<=> overload for
+ // This forward declaration is used by the operator<=> overload for
// tuple-like types.
- template<typename _Cat, typename _Tp, typename _Up>
+ template<typename _Cat, typename _Tp, typename _Up, typename _IndexSeq>
constexpr _Cat
- __tuple_cmp(const _Tp&, const _Up&, index_sequence<>);
-
- template<typename _Cat, typename _Tp, typename _Up,
- size_t _Idx0, size_t... _Idxs>
- constexpr _Cat
- __tuple_cmp(const _Tp& __t, const _Up& __u,
- index_sequence<_Idx0, _Idxs...>);
+ __tuple_cmp(const _Tp& __t, const _Up& __u, _IndexSeq);
#endif // C++23
/**
@@ -1848,7 +1843,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<__tuple_like _UTuple>
requires (!__is_tuple_v<_UTuple>)
friend constexpr bool
- operator==(const tuple& __t, const _UTuple& __u)
+ operator== [[nodiscard]] (const tuple& __t, const _UTuple& __u)
{
static_assert(sizeof...(_Elements) == tuple_size_v<_UTuple>,
"tuple objects can only be compared if they have equal sizes.");
@@ -2521,6 +2516,58 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#endif
+#if __cpp_lib_three_way_comparison
+ template<typename... _Tps, typename... _Ups>
+ requires (sizeof...(_Tps) == sizeof...(_Ups))
+ && (requires (const _Tps& __t, const _Ups& __u) {
+ { __t == __u } -> __detail::__boolean_testable;
+ } && ...)
+ constexpr bool
+ operator== [[nodiscard]] (const tuple<_Tps...>& __t,
+ const tuple<_Ups...>& __u)
+ {
+ return [&]<size_t... _Inds>(index_sequence<_Inds...>) {
+ // Fold == over the tuples until non-equal elements are found.
+ return ((std::get<_Inds>(__t) == std::get<_Inds>(__u)) && ...);
+ }(index_sequence_for<_Tps...>{});
+ }
+
+ template<typename _Cat, typename _Tp, typename _Up, typename _IndexSeq>
+ [[nodiscard]]
+ constexpr _Cat
+ __tuple_cmp(const _Tp& __t, const _Up& __u, _IndexSeq __indices)
+ {
+ _Cat __c = _Cat::equivalent;
+
+ // Set __c to the comparison result of two corresponding elements.
+ // Return true they are equivalent.
+ auto __cmp = [&]<size_t _Ind>(integral_constant<size_t, _Ind>) {
+ __c = __detail::__synth3way(std::get<_Ind>(__t), std::get<_Ind>(__u));
+ return __c == 0;
+ };
+
+ [&]<size_t... _Inds>(index_sequence<_Inds...>) {
+ // Fold __cmp over the tuples until non-equivalent elements are found.
+ (void)(__cmp(integral_constant<size_t, _Inds>{}) && ...);
+ }(__indices);
+
+ return __c;
+ }
+
+ template<typename... _Tps, typename... _Ups>
+ requires (sizeof...(_Tps) == sizeof...(_Ups))
+ && (requires { typename __detail::__synth3way_t<_Tps, _Ups>; } && ...)
+ constexpr
+ common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>
+ operator<=> [[nodiscard]] (const tuple<_Tps...>& __t,
+ const tuple<_Ups...>& __u)
+ {
+ using _Cat
+ = common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>;
+ return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Tps...>());
+ }
+#else
+
// This class performs the comparison operations on tuples
template<typename _Tp, typename _Up, size_t __i, size_t __size>
struct __tuple_compare
@@ -2552,6 +2599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
};
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator==(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
@@ -2564,36 +2612,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __compare::__eq(__t, __u);
}
-#if __cpp_lib_three_way_comparison
- template<typename _Cat, typename _Tp, typename _Up>
- constexpr _Cat
- __tuple_cmp(const _Tp&, const _Up&, index_sequence<>)
- { return _Cat::equivalent; }
-
- template<typename _Cat, typename _Tp, typename _Up,
- size_t _Idx0, size_t... _Idxs>
- constexpr _Cat
- __tuple_cmp(const _Tp& __t, const _Up& __u,
- index_sequence<_Idx0, _Idxs...>)
- {
- auto __c
- = __detail::__synth3way(std::get<_Idx0>(__t), std::get<_Idx0>(__u));
- if (__c != 0)
- return __c;
- return std::__tuple_cmp<_Cat>(__t, __u, index_sequence<_Idxs...>());
- }
-
- template<typename... _Tps, typename... _Ups>
- constexpr
- common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>
- operator<=>(const tuple<_Tps...>& __t, const tuple<_Ups...>& __u)
- {
- using _Cat
- = common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>;
- return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Tps...>());
- }
-#else
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator<(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
@@ -2607,24 +2627,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator!=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return !(__t == __u); }
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator>(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return __u < __t; }
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator<=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return !(__u < __t); }
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator>=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
diff --git a/libstdc++-v3/include/std/utility b/libstdc++-v3/include/std/utility
index 212513f6f48..56467160a2f 100644
--- a/libstdc++-v3/include/std/utility
+++ b/libstdc++-v3/include/std/utility
@@ -93,6 +93,7 @@
#define __glibcxx_want_tuples_by_type
#define __glibcxx_want_unreachable
#define __glibcxx_want_tuple_like
+#define __glibcxx_want_constrained_equality
#include <bits/version.h>
namespace std _GLIBCXX_VISIBILITY(default)
diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index f79d95db7a8..5ba6d9d42e3 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -33,6 +33,7 @@
#define __glibcxx_want_freestanding_variant
#define __glibcxx_want_variant
+#define __glibcxx_want_constrained_equality
#include <bits/version.h>
#ifdef __cpp_lib_variant // C++ >= 17
@@ -1236,9 +1237,19 @@ namespace __variant
struct monostate { };
+#if __cpp_lib_concepts
+# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP) \
+ requires ((requires (const TYPES& __t) { \
+ { __t OP __t } -> __detail::__boolean_testable; }) && ...)
+#else
+# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP)
+#endif
+
#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP, __NAME) \
template<typename... _Types> \
- constexpr bool operator __OP(const variant<_Types...>& __lhs, \
+ _VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \
+ constexpr bool \
+ operator __OP [[nodiscard]] (const variant<_Types...>& __lhs, \
const variant<_Types...>& __rhs) \
{ \
bool __ret = true; \
@@ -1690,21 +1701,6 @@ namespace __variant
template<size_t _Np, typename _Vp>
friend constexpr decltype(auto)
__detail::__variant::__get(_Vp&& __v) noexcept;
-
-#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \
- template<typename... _Tp> \
- friend constexpr bool \
- operator __OP(const variant<_Tp...>& __lhs, \
- const variant<_Tp...>& __rhs);
-
- _VARIANT_RELATION_FUNCTION_TEMPLATE(<)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(<=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(==)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(!=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(>=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(>)
-
-#undef _VARIANT_RELATION_FUNCTION_TEMPLATE
};
template<size_t _Np, typename... _Types>
diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
new file mode 100644
index 00000000000..0e325618008
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
@@ -0,0 +1,258 @@
+// { dg-do compile { target c++20 } }
+
+#include <optional>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing"
+#elif __cpp_lib_constrained_equality != 202403
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T, typename U = T>
+concept eq_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t == u;
+ *t == u;
+ t == *u;
+};
+
+template<typename T, typename U = T>
+concept ne_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t != u;
+ *t != u;
+ t != *u;
+};
+
+template<typename T, typename U = T>
+concept lt_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t < u;
+ *t < u;
+ t < *u;
+};
+
+template<typename T, typename U = T>
+concept le_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t <= u;
+ *t <= u;
+ t <= *u;
+};
+
+template<typename T, typename U = T>
+concept gt_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t > u;
+ *t > u;
+ t > *u;
+};
+
+template<typename T, typename U = T>
+concept ge_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t >= u;
+ *t >= u;
+ t >= *u;
+};
+
+static_assert( eq_comparable<int> );
+static_assert( ne_comparable<int> );
+static_assert( lt_comparable<int> );
+static_assert( le_comparable<int> );
+static_assert( gt_comparable<int> );
+static_assert( ge_comparable<int> );
+static_assert( eq_comparable<int, long> );
+static_assert( ne_comparable<int, long> );
+static_assert( lt_comparable<int, long> );
+static_assert( le_comparable<int, long> );
+static_assert( gt_comparable<int, long> );
+static_assert( ge_comparable<int, long> );
+static_assert( eq_comparable<short, int> );
+static_assert( ne_comparable<short, int> );
+static_assert( lt_comparable<short, int> );
+static_assert( le_comparable<short, int> );
+static_assert( gt_comparable<short, int> );
+static_assert( ge_comparable<short, int> );
+
+struct A { };
+static_assert( ! eq_comparable<A> );
+static_assert( ! ne_comparable<A> );
+static_assert( ! lt_comparable<A> );
+static_assert( ! le_comparable<A> );
+static_assert( ! gt_comparable<A> );
+static_assert( ! ge_comparable<A> );
+static_assert( ! eq_comparable<A, A> );
+static_assert( ! ne_comparable<A, A> );
+static_assert( ! lt_comparable<A, A> );
+static_assert( ! le_comparable<A, A> );
+static_assert( ! gt_comparable<A, A> );
+static_assert( ! ge_comparable<A, A> );
+static_assert( ! eq_comparable<A, int> );
+static_assert( ! ne_comparable<A, int> );
+static_assert( ! lt_comparable<A, int> );
+static_assert( ! le_comparable<A, int> );
+static_assert( ! gt_comparable<A, int> );
+static_assert( ! ge_comparable<A, int> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! eq_comparable<B> );
+static_assert( ! ne_comparable<B> );
+static_assert( ! lt_comparable<B> );
+static_assert( ! le_comparable<B> );
+static_assert( ! gt_comparable<B> );
+static_assert( ! ge_comparable<B> );
+static_assert( ! eq_comparable<B, B> );
+static_assert( ! ne_comparable<B, B> );
+static_assert( ! lt_comparable<B, B> );
+static_assert( ! le_comparable<B, B> );
+static_assert( ! gt_comparable<B, B> );
+static_assert( ! ge_comparable<B, B> );
+static_assert( ! eq_comparable<B, int> );
+static_assert( ! ne_comparable<B, int> );
+static_assert( ! lt_comparable<B, int> );
+static_assert( ! le_comparable<B, int> );
+static_assert( ! gt_comparable<B, int> );
+static_assert( ! ge_comparable<B, int> );
+
+struct C { };
+bool operator==(C, C);
+static_assert( eq_comparable<C> );
+static_assert( ne_comparable<C> );
+static_assert( ! lt_comparable<C> );
+static_assert( ! le_comparable<C> );
+static_assert( ! gt_comparable<C> );
+static_assert( ! ge_comparable<C> );
+static_assert( eq_comparable<C, C> );
+static_assert( ne_comparable<C, C> );
+static_assert( eq_comparable<C, C> );
+static_assert( ne_comparable<C, C> );
+
+struct D { };
+int operator==(D, D);
+bool operator!=(D, D) = delete;
+static_assert( eq_comparable<D> );
+static_assert( ! ne_comparable<D> );
+static_assert( ! lt_comparable<D> );
+static_assert( ! le_comparable<D> );
+static_assert( ! gt_comparable<D> );
+static_assert( ! ge_comparable<D> );
+static_assert( eq_comparable<D, D> );
+static_assert( ! ne_comparable<D, D> );
+static_assert( eq_comparable<D, D> );
+static_assert( ! ne_comparable<D, D> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! eq_comparable<E> );
+static_assert( ! ne_comparable<E> );
+static_assert( ! lt_comparable<E> );
+static_assert( ! le_comparable<E> );
+static_assert( ! gt_comparable<E> );
+static_assert( ! ge_comparable<E> );
+static_assert( ! eq_comparable<E, E> );
+static_assert( ! eq_comparable<E, E> );
+
+struct F { };
+bool operator<(F, F);
+void operator>(F, F);
+static_assert( ! eq_comparable<F> );
+static_assert( ! ne_comparable<F> );
+static_assert( lt_comparable<F> );
+static_assert( ! le_comparable<F> );
+static_assert( ! gt_comparable<F> );
+static_assert( ! ge_comparable<F> );
+static_assert( lt_comparable<F, F> );
+static_assert( lt_comparable<F, F> );
+
+struct G { };
+bool operator<=(G, G);
+void operator<(G, G);
+static_assert( ! eq_comparable<G> );
+static_assert( ! ne_comparable<G> );
+static_assert( ! lt_comparable<G> );
+static_assert( le_comparable<G> );
+static_assert( ! gt_comparable<G> );
+static_assert( ! ge_comparable<G> );
+static_assert( le_comparable<G, G> );
+static_assert( le_comparable<G, G> );
+
+struct H { };
+bool operator>(H, H);
+void operator>=(H, H);
+static_assert( ! eq_comparable<H> );
+static_assert( ! ne_comparable<H> );
+static_assert( ! lt_comparable<H> );
+static_assert( ! le_comparable<H> );
+static_assert( gt_comparable<H> );
+static_assert( ! ge_comparable<H> );
+static_assert( gt_comparable<H, H> );
+static_assert( gt_comparable<H, H> );
+
+struct I { };
+bool operator>=(I, I);
+void operator<=(I, I);
+static_assert( ! eq_comparable<I> );
+static_assert( ! ne_comparable<I> );
+static_assert( ! lt_comparable<I> );
+static_assert( ! le_comparable<I> );
+static_assert( ! gt_comparable<I> );
+static_assert( ge_comparable<I> );
+static_assert( ge_comparable<I, I> );
+static_assert( ge_comparable<I, I> );
+
+struct J { };
+bool operator==(J, J);
+std::weak_ordering operator<=>(J, J);
+static_assert( eq_comparable<J> );
+static_assert( ne_comparable<J> );
+static_assert( lt_comparable<J> );
+static_assert( le_comparable<J> );
+static_assert( gt_comparable<J> );
+static_assert( ge_comparable<J> );
+
+struct K { };
+int operator==(K, K); // non-bool prevents synthesis of !=
+void operator<=(K, K);
+std::weak_ordering operator<=>(K, K);
+static_assert( eq_comparable<K> );
+static_assert( ! ne_comparable<K> );
+static_assert( lt_comparable<K> );
+static_assert( ! le_comparable<K> );
+static_assert( gt_comparable<K> );
+static_assert( ge_comparable<K> );
+
+bool operator==(A, B);
+static_assert( eq_comparable<A, B> );
+static_assert( eq_comparable<B, A> );
+static_assert( ne_comparable<A, B> );
+static_assert( ne_comparable<B, A> );
+static_assert( ! lt_comparable<A, B> );
+static_assert( ! le_comparable<A, B> );
+static_assert( ! gt_comparable<A, B> );
+static_assert( ! ge_comparable<A, B> );
+
+int operator==(C, D); // non-bool prevents synthesis of != and reversed args
+static_assert( eq_comparable<C, D> );
+static_assert( ! eq_comparable<D, C> );
+static_assert( ! ne_comparable<C, D> );
+static_assert( ! ne_comparable<D, C> );
+static_assert( ! lt_comparable<C, D> );
+static_assert( ! le_comparable<C, D> );
+static_assert( ! gt_comparable<C, D> );
+static_assert( ! ge_comparable<C, D> );
+
+std::weak_ordering operator<=>(E, F);
+static_assert( ! eq_comparable<E, F> );
+static_assert( ! eq_comparable<F, E> );
+static_assert( ! ne_comparable<E, F> );
+static_assert( ! ne_comparable<F, E> );
+static_assert( lt_comparable<E, F> );
+static_assert( le_comparable<E, F> );
+static_assert( gt_comparable<E, F> );
+static_assert( ge_comparable<E, F> );
+static_assert( lt_comparable<F, E> );
+static_assert( le_comparable<F, E> );
+static_assert( gt_comparable<F, E> );
+static_assert( ge_comparable<F, E> );
diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
new file mode 100644
index 00000000000..a35dbd265a7
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
@@ -0,0 +1,48 @@
+// { dg-do compile { target c++20 } }
+
+#include <utility>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing"
+#elif __cpp_lib_constrained_equality != 202403
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T>
+concept equality_comparable = requires (const T& t) { t == t; t != t; };
+
+static_assert( equality_comparable<std::pair<int, long>> );
+
+struct A { };
+static_assert( ! equality_comparable<std::pair<A, long>> );
+static_assert( ! equality_comparable<std::pair<int, A>> );
+static_assert( ! equality_comparable<std::pair<A, A>> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! equality_comparable<std::pair<B, long>> );
+static_assert( ! equality_comparable<std::pair<int, B>> );
+static_assert( ! equality_comparable<std::pair<B, B>> );
+static_assert( ! equality_comparable<std::pair<A, B>> );
+
+struct C { };
+int operator==(C, C);
+static_assert( equality_comparable<std::pair<C, long>> );
+static_assert( equality_comparable<std::pair<int, C>> );
+static_assert( equality_comparable<std::pair<C, C>> );
+static_assert( ! equality_comparable<std::pair<A, C>> );
+
+struct D { };
+bool operator==(D, D);
+bool operator!=(D, D) = delete;
+static_assert( equality_comparable<std::pair<D, long>> );
+static_assert( equality_comparable<std::pair<int, D>> );
+static_assert( equality_comparable<std::pair<D, D>> );
+static_assert( equality_comparable<std::pair<C, D>> );
+static_assert( ! equality_comparable<std::pair<A, C>> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! equality_comparable<std::pair<E, long>> );
+static_assert( ! equality_comparable<std::pair<int, E>> );
+static_assert( ! equality_comparable<std::pair<E, E>> );
diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
new file mode 100644
index 00000000000..47035ab18ba
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
@@ -0,0 +1,50 @@
+// { dg-do compile { target c++20 } }
+
+#include <tuple>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing"
+#elif __cpp_lib_constrained_equality != 202403
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T>
+concept equality_comparable = requires (const T& t) { t == t; t != t; };
+
+static_assert( equality_comparable<std::tuple<>> );
+static_assert( equality_comparable<std::tuple<int>> );
+static_assert( equality_comparable<std::tuple<int, long>> );
+static_assert( equality_comparable<std::tuple<int, long, short>> );
+
+struct A { };
+static_assert( ! equality_comparable<std::tuple<A>> );
+static_assert( ! equality_comparable<std::tuple<int, A>> );
+static_assert( ! equality_comparable<std::tuple<A, A>> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! equality_comparable<std::tuple<B>> );
+static_assert( ! equality_comparable<std::tuple<int, B>> );
+static_assert( ! equality_comparable<std::tuple<B, B>> );
+
+struct C { };
+int operator==(C, C);
+static_assert( equality_comparable<std::tuple<C>> );
+static_assert( equality_comparable<std::tuple<int, C>> );
+static_assert( equality_comparable<std::tuple<C, C>> );
+static_assert( ! equality_comparable<std::tuple<A, C>> );
+
+struct D { };
+bool operator==(D, D);
+bool operator!=(D, D) = delete;
+static_assert( equality_comparable<std::tuple<D>> );
+static_assert( equality_comparable<std::tuple<int, D>> );
+static_assert( equality_comparable<std::tuple<D, D>> );
+static_assert( equality_comparable<std::tuple<C, D>> );
+static_assert( ! equality_comparable<std::tuple<A, C>> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! equality_comparable<std::tuple<E>> );
+static_assert( ! equality_comparable<std::tuple<int, E>> );
+static_assert( ! equality_comparable<std::tuple<E, E>> );
diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc
index 9f8a0f91785..7ae7f42d1a1 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc
@@ -1,4 +1,5 @@
-// { dg-do compile { target c++11 } }
+// { dg-do compile { target { c++11 && c++17_down } } }
+// Not valid in C++20, because TwistedLogic doesn't model boolean-testable.
// Copyright (C) 2014-2024 Free Software Foundation, Inc.
//
@@ -49,8 +50,5 @@ TwistedLogic operator<(const Compares&, const Compares&) { return {false}; }
auto a = std::make_tuple(nullptr, Compares{}, 2, 'U');
auto b = a == a;
-#if ! __cpp_lib_three_way_comparison
-// Not valid in C++20, because TwistedLogic doesn't model boolean-testable.
auto c = std::make_tuple("", Compares{}, 2, 'U');
auto d = c < c;
-#endif
diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc
index 19617a5676e..8ea7ed0b797 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc
@@ -50,5 +50,4 @@ auto a = std::make_tuple(nullptr, Compares{}, 2, 'U');
auto b = a < a;
// { dg-error "no match for 'operator<'" "" { target c++20 } 0 }
-// { dg-error "no match for .*_Synth3way|in requirements" "" { target c++20 } 0 }
// { dg-error "ordered comparison" "" { target c++17_down } 0 }
diff --git a/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc
new file mode 100644
index 00000000000..95e8f754d1e
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc
@@ -0,0 +1,175 @@
+// { dg-do compile { target c++20 } }
+
+#include <variant>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing"
+#elif __cpp_lib_constrained_equality != 202403
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T, typename... U>
+concept eq_comparable
+= requires (const std::variant<int, T, U...>& t) { t == t; };
+
+template<typename T, typename... U>
+concept ne_comparable
+= requires (const std::variant<int, T, U...>& t) { t != t; };
+
+template<typename T, typename... U>
+concept lt_comparable
+= requires (const std::variant<int, T, U...>& t) { t < t; };
+
+template<typename T, typename... U>
+concept le_comparable
+= requires (const std::variant<int, T, U...>& t) { t <= t; };
+
+template<typename T, typename... U>
+concept gt_comparable
+= requires (const std::variant<int, T, U...>& t) { t > t; };
+
+template<typename T, typename... U>
+concept ge_comparable
+= requires (const std::variant<int, T, U...>& t) { t >= t; };
+
+static_assert( eq_comparable<int> );
+static_assert( ne_comparable<int> );
+static_assert( lt_comparable<int> );
+static_assert( le_comparable<int> );
+static_assert( gt_comparable<int> );
+static_assert( ge_comparable<int> );
+static_assert( eq_comparable<int, long> );
+static_assert( ne_comparable<int, long> );
+static_assert( lt_comparable<int, long> );
+static_assert( le_comparable<int, long> );
+static_assert( gt_comparable<int, long> );
+static_assert( ge_comparable<int, long> );
+static_assert( eq_comparable<short, int> );
+static_assert( ne_comparable<short, int> );
+static_assert( lt_comparable<short, int> );
+static_assert( le_comparable<short, int> );
+static_assert( gt_comparable<short, int> );
+static_assert( ge_comparable<short, int> );
+static_assert( eq_comparable<std::monostate, char, int> );
+static_assert( ne_comparable<std::monostate, char, int> );
+static_assert( lt_comparable<std::monostate, char, int> );
+static_assert( le_comparable<std::monostate, char, int> );
+static_assert( gt_comparable<std::monostate, char, int> );
+static_assert( ge_comparable<std::monostate, char, int> );
+
+struct A { };
+static_assert( ! eq_comparable<A> );
+static_assert( ! ne_comparable<A> );
+static_assert( ! lt_comparable<A> );
+static_assert( ! le_comparable<A> );
+static_assert( ! gt_comparable<A> );
+static_assert( ! ge_comparable<A> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! eq_comparable<B> );
+static_assert( ! ne_comparable<B> );
+static_assert( ! lt_comparable<B> );
+static_assert( ! le_comparable<B> );
+static_assert( ! gt_comparable<B> );
+static_assert( ! ge_comparable<B> );
+
+struct C { };
+bool operator==(C, C);
+static_assert( eq_comparable<C> );
+static_assert( ne_comparable<C> );
+static_assert( ! lt_comparable<C> );
+static_assert( ! le_comparable<C> );
+static_assert( ! gt_comparable<C> );
+static_assert( ! ge_comparable<C> );
+static_assert( ne_comparable<C, C> );
+
+struct D { };
+int operator==(D, D); // variant's operator== returns bool despite int here
+bool operator!=(D, D) = delete;
+static_assert( eq_comparable<D> );
+static_assert( ne_comparable<D> ); // variant's operator== can be used
+static_assert( ! lt_comparable<D> );
+static_assert( ! le_comparable<D> );
+static_assert( ! gt_comparable<D> );
+static_assert( ! ge_comparable<D> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! eq_comparable<E> );
+static_assert( ! ne_comparable<E> );
+static_assert( ! lt_comparable<E> );
+static_assert( ! le_comparable<E> );
+static_assert( ! gt_comparable<E> );
+static_assert( ! ge_comparable<E> );
+
+struct F { };
+bool operator<(F, F);
+void operator>(F, F);
+static_assert( ! eq_comparable<F> );
+static_assert( ! ne_comparable<F> );
+static_assert( lt_comparable<F> );
+static_assert( ! le_comparable<F> );
+static_assert( ! gt_comparable<F> );
+static_assert( ! ge_comparable<F> );
+
+struct G { };
+bool operator<=(G, G);
+void operator<(G, G);
+static_assert( ! eq_comparable<G> );
+static_assert( ! ne_comparable<G> );
+static_assert( ! lt_comparable<G> );
+static_assert( le_comparable<G> );
+static_assert( ! gt_comparable<G> );
+static_assert( ! ge_comparable<G> );
+
+struct H { };
+bool operator>(H, H);
+void operator>=(H, H);
+static_assert( ! eq_comparable<H> );
+static_assert( ! ne_comparable<H> );
+static_assert( ! lt_comparable<H> );
+static_assert( ! le_comparable<H> );
+static_assert( gt_comparable<H> );
+static_assert( ! ge_comparable<H> );
+static_assert( gt_comparable<H, H> );
+static_assert( gt_comparable<H, H> );
+
+struct I { };
+bool operator>=(I, I);
+void operator<=(I, I);
+static_assert( ! eq_comparable<I> );
+static_assert( ! ne_comparable<I> );
+static_assert( ! lt_comparable<I> );
+static_assert( ! le_comparable<I> );
+static_assert( ! gt_comparable<I> );
+static_assert( ge_comparable<I> );
+static_assert( ge_comparable<I, I> );
+static_assert( ge_comparable<I, I> );
+
+struct J { };
+bool operator==(J, J);
+std::weak_ordering operator<=>(J, J);
+static_assert( eq_comparable<J> );
+static_assert( ne_comparable<J> );
+static_assert( lt_comparable<J> );
+static_assert( le_comparable<J> );
+static_assert( gt_comparable<J> );
+static_assert( ge_comparable<J> );
+
+struct K { };
+int operator==(K, K); // variant's operator== returns bool despite int here
+void operator<=(K, K);
+std::weak_ordering operator<=>(K, K);
+static_assert( eq_comparable<K> );
+static_assert( ne_comparable<K> ); // variant's operator== can be used
+static_assert( lt_comparable<K> );
+static_assert( ! le_comparable<K> );
+static_assert( gt_comparable<K> );
+static_assert( ge_comparable<K> );
+static_assert( eq_comparable<K, J> );
+static_assert( ne_comparable<K, J> );
+static_assert( lt_comparable<K, J> );
+static_assert( ! le_comparable<K, J> );
+static_assert( gt_comparable<K, J> );
+static_assert( ge_comparable<K, J> );
--
2.44.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 4/4] libstdc++: Simplify std::variant comparison operators
2024-04-10 8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
2024-04-10 8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
2024-04-10 8:45 ` [PATCH 3/4] libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant Jonathan Wakely
@ 2024-04-10 8:45 ` Jonathan Wakely
2024-04-10 10:45 ` [PATCH 5/4] libstdc++: Rewrite std::variant comparisons without macros Jonathan Wakely
2024-05-07 13:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
2024-04-15 18:29 ` [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
3 siblings, 2 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10 8:45 UTC (permalink / raw)
To: libstdc++, gcc-patches
Tested x86_64-linux.
This is just a minor clean-up and could wait for stage 1.
-- >8 --
libstdc++-v3/ChangeLog:
* include/std/variant (_VARIANT_RELATION_FUNCTION_TEMPLATE):
Simplify.
---
libstdc++-v3/include/std/variant | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 5ba6d9d42e3..2be0f0c1db7 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -1245,7 +1245,7 @@ namespace __variant
# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP)
#endif
-#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP, __NAME) \
+#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \
template<typename... _Types> \
_VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \
constexpr bool \
@@ -1262,22 +1262,20 @@ namespace __variant
{ \
auto& __this_mem = std::get<__rhs_index>(__lhs); \
__ret = __this_mem __OP __rhs_mem; \
+ return; \
} \
- else \
- __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
} \
- else \
- __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
+ __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
}, __rhs); \
return __ret; \
}
- _VARIANT_RELATION_FUNCTION_TEMPLATE(<, less)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(<=, less_equal)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(==, equal)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(!=, not_equal)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(>=, greater_equal)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(>, greater)
+ _VARIANT_RELATION_FUNCTION_TEMPLATE(<)
+ _VARIANT_RELATION_FUNCTION_TEMPLATE(<=)
+ _VARIANT_RELATION_FUNCTION_TEMPLATE(==)
+ _VARIANT_RELATION_FUNCTION_TEMPLATE(!=)
+ _VARIANT_RELATION_FUNCTION_TEMPLATE(>=)
+ _VARIANT_RELATION_FUNCTION_TEMPLATE(>)
#undef _VARIANT_RELATION_FUNCTION_TEMPLATE
--
2.44.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 5/4] libstdc++: Rewrite std::variant comparisons without macros
2024-04-10 8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
@ 2024-04-10 10:45 ` Jonathan Wakely
2024-05-07 13:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
1 sibling, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10 10:45 UTC (permalink / raw)
To: libstdc++, gcc-patches
I think this is considerably nicer than the macro version, but it can
definitely wait for stage 1.
-- >8 --
libstdc++-v3/ChangeLog:
* include/std/variant (__detail::__variant::__compare): New
function template.
(operator==, operator!=, operator<, operator>, operator<=)
(operator>=): Replace macro definition with handwritten function
calling __detail::__variant::__compare.
(operator<=>): Call __detail::__variant::__compare.
---
libstdc++-v3/include/std/variant | 167 +++++++++++++++++++++----------
1 file changed, 114 insertions(+), 53 deletions(-)
diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 87a119df8b5..c2277e8831a 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -48,6 +48,7 @@
#include <bits/stl_construct.h>
#include <bits/utility.h> // in_place_index_t
#if __cplusplus >= 202002L
+# include <concepts>
# include <compare>
#endif
@@ -1237,47 +1238,119 @@ namespace __variant
struct monostate { };
-#if __cpp_lib_concepts
-# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP) \
- requires ((requires (const TYPES& __t) { \
- { __t OP __t } -> __detail::__boolean_testable; }) && ...)
-#else
-# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP)
-#endif
+namespace __detail::__variant
+{
+ template<typename _Ret, typename _Vp, typename _Op>
+ constexpr _Ret
+ __compare(_Ret __ret, const _Vp& __lhs, const _Vp& __rhs, _Op __op)
+ {
+ __variant::__raw_idx_visit(
+ [&__ret, &__lhs, __op] (auto&& __rhs_mem, auto __rhs_index) mutable
+ {
+ if constexpr (__rhs_index != variant_npos)
+ {
+ if (__lhs.index() == __rhs_index.value)
+ {
+ auto& __this_mem = std::get<__rhs_index>(__lhs);
+ __ret = __op(__this_mem, __rhs_mem);
+ return;
+ }
+ }
+ __ret = __op(__lhs.index() + 1, __rhs_index + 1);
+ }, __rhs);
+ return __ret;
+ }
+} // namespace __detail::__variant
-#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \
- template<typename... _Types> \
- _VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \
- constexpr bool \
- operator __OP [[nodiscard]] (const variant<_Types...>& __lhs, \
- const variant<_Types...>& __rhs) \
- { \
- bool __ret = true; \
- __detail::__variant::__raw_idx_visit( \
- [&__ret, &__lhs] (auto&& __rhs_mem, auto __rhs_index) mutable \
- { \
- if constexpr (__rhs_index != variant_npos) \
- { \
- if (__lhs.index() == __rhs_index) \
- { \
- auto& __this_mem = std::get<__rhs_index>(__lhs); \
- __ret = __this_mem __OP __rhs_mem; \
- return; \
- } \
- } \
- __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
- }, __rhs); \
- return __ret; \
+ template<typename... _Types>
+#if __cpp_lib_concepts
+ requires ((requires (const _Types& __t) {
+ { __t == __t } -> convertible_to<bool>; }) && ...)
+#endif
+ constexpr bool
+ operator== [[nodiscard]] (const variant<_Types...>& __lhs,
+ const variant<_Types...>& __rhs)
+ {
+ return __detail::__variant::__compare(true, __lhs, __rhs,
+ [](auto&& __l, auto&& __r) {
+ return __l == __r;
+ });
}
- _VARIANT_RELATION_FUNCTION_TEMPLATE(<)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(<=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(==)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(!=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(>=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(>)
+ template<typename... _Types>
+#if __cpp_lib_concepts
+ requires ((requires (const _Types& __t) {
+ { __t != __t } -> convertible_to<bool>; }) && ...)
+#endif
+ constexpr bool
+ operator!= [[nodiscard]] (const variant<_Types...>& __lhs,
+ const variant<_Types...>& __rhs)
+ {
+ return __detail::__variant::__compare(true, __lhs, __rhs,
+ [](auto&& __l, auto&& __r) {
+ return __l != __r;
+ });
+ }
-#undef _VARIANT_RELATION_FUNCTION_TEMPLATE
+ template<typename... _Types>
+#if __cpp_lib_concepts
+ requires ((requires (const _Types& __t) {
+ { __t < __t } -> convertible_to<bool>; }) && ...)
+#endif
+ constexpr bool
+ operator< [[nodiscard]] (const variant<_Types...>& __lhs,
+ const variant<_Types...>& __rhs)
+ {
+ return __detail::__variant::__compare(true, __lhs, __rhs,
+ [](auto&& __l, auto&& __r) {
+ return __l < __r;
+ });
+ }
+
+ template<typename... _Types>
+#if __cpp_lib_concepts
+ requires ((requires (const _Types& __t) {
+ { __t <= __t } -> convertible_to<bool>; }) && ...)
+#endif
+ constexpr bool
+ operator<= [[nodiscard]] (const variant<_Types...>& __lhs,
+ const variant<_Types...>& __rhs)
+ {
+ return __detail::__variant::__compare(true, __lhs, __rhs,
+ [](auto&& __l, auto&& __r) {
+ return __l <= __r;
+ });
+ }
+
+ template<typename... _Types>
+#if __cpp_lib_concepts
+ requires ((requires (const _Types& __t) {
+ { __t > __t } -> convertible_to<bool>; }) && ...)
+#endif
+ constexpr bool
+ operator> [[nodiscard]] (const variant<_Types...>& __lhs,
+ const variant<_Types...>& __rhs)
+ {
+ return __detail::__variant::__compare(true, __lhs, __rhs,
+ [](auto&& __l, auto&& __r) {
+ return __l > __r;
+ });
+ }
+
+ template<typename... _Types>
+#if __cpp_lib_concepts
+ requires ((requires (const _Types& __t) {
+ { __t >= __t } -> convertible_to<bool>; }) && ...)
+#endif
+ constexpr bool
+ operator>= [[nodiscard]] (const variant<_Types...>& __lhs,
+ const variant<_Types...>& __rhs)
+ {
+ return __detail::__variant::__compare(true, __lhs, __rhs,
+ [](auto&& __l, auto&& __r) {
+ return __l >= __r;
+ });
+ }
constexpr bool operator==(monostate, monostate) noexcept { return true; }
@@ -1290,22 +1363,10 @@ namespace __variant
{
common_comparison_category_t<compare_three_way_result_t<_Types>...> __ret
= strong_ordering::equal;
-
- __detail::__variant::__raw_idx_visit(
- [&__ret, &__v] (auto&& __w_mem, auto __w_index) mutable
- {
- if constexpr (__w_index != variant_npos)
- {
- if (__v.index() == __w_index)
- {
- auto& __this_mem = std::get<__w_index>(__v);
- __ret = __this_mem <=> __w_mem;
- return;
- }
- }
- __ret = (__v.index() + 1) <=> (__w_index + 1);
- }, __w);
- return __ret;
+ return __detail::__variant::__compare(__ret, __v, __w,
+ [](auto&& __l, auto&& __r) {
+ return __l <=> __r;
+ });
}
constexpr strong_ordering
--
2.44.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386]
2024-04-10 8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
` (2 preceding siblings ...)
2024-04-10 8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
@ 2024-04-15 18:29 ` Jonathan Wakely
3 siblings, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-15 18:29 UTC (permalink / raw)
To: libstdc++, gcc-patches
Pushed to trunk now.
On Wed, 10 Apr 2024 at 09:51, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> Tested x86_64-linux.
>
> Since this only affects C++20 and later it seems OK for trunk now.
>
> -- >8 --
>
> I'm only treating this as a DR for C++20 for now, because it's less work
> and only requires changes to operator== and operator<=>. To do this for
> older standards would require changes to the six relational operators
> used pre-C++20.
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/113386
> * include/bits/stl_pair.h (operator==, operator<=>): Support
> heterogeneous comparisons, as per LWG 3865.
> * testsuite/20_util/pair/comparison_operators/lwg3865.cc: New
> test.
> ---
> libstdc++-v3/include/bits/stl_pair.h | 32 ++++++++++++++-----
> .../pair/comparison_operators/lwg3865.cc | 15 +++++++++
> 2 files changed, 39 insertions(+), 8 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
>
> diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
> index 4f5c8389fa6..45317417c9c 100644
> --- a/libstdc++-v3/include/bits/stl_pair.h
> +++ b/libstdc++-v3/include/bits/stl_pair.h
> @@ -1000,23 +1000,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _T1, typename _T2> pair(_T1, _T2) -> pair<_T1, _T2>;
> #endif
>
> - /// Two pairs of the same type are equal iff their members are equal.
> - template<typename _T1, typename _T2>
> +#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 3865. Sorting a range of pairs
> +
> + /// Two pairs are equal iff their members are equal.
> + template<typename _T1, typename _T2, typename _U1, typename _U2>
> inline _GLIBCXX_CONSTEXPR bool
> - operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
> + operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
> { return __x.first == __y.first && __x.second == __y.second; }
>
> -#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
> - template<typename _T1, typename _T2>
> - constexpr common_comparison_category_t<__detail::__synth3way_t<_T1>,
> - __detail::__synth3way_t<_T2>>
> - operator<=>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
> + /** Defines a lexicographical order for pairs.
> + *
> + * For two pairs of comparable types, `P` is ordered before `Q` if
> + * `P.first` is less than `Q.first`, or if `P.first` and `Q.first`
> + * are equivalent (neither is less than the other) and `P.second` is
> + * less than `Q.second`.
> + */
> + template<typename _T1, typename _T2, typename _U1, typename _U2>
> + constexpr common_comparison_category_t<__detail::__synth3way_t<_T1, _U1>,
> + __detail::__synth3way_t<_T2, _U2>>
> + operator<=>(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
> {
> if (auto __c = __detail::__synth3way(__x.first, __y.first); __c != 0)
> return __c;
> return __detail::__synth3way(__x.second, __y.second);
> }
> #else
> + /// Two pairs of the same type are equal iff their members are equal.
> + template<typename _T1, typename _T2>
> + inline _GLIBCXX_CONSTEXPR bool
> + operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
> + { return __x.first == __y.first && __x.second == __y.second; }
> +
> /** Defines a lexicographical order for pairs.
> *
> * For two pairs of the same type, `P` is ordered before `Q` if
> diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
> new file mode 100644
> index 00000000000..2bbd54af192
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
> @@ -0,0 +1,15 @@
> +// { dg-do run { target c++20 } }
> +
> +// LWG 3865. Sorting a range of pairs
> +
> +#include <utility>
> +#include <testsuite_hooks.h>
> +
> +int main()
> +{
> + std::pair<int, int> p(1, 2);
> + std::pair<int&, int&> p2(p.first, p.second);
> + VERIFY( p == p2 );
> + VERIFY( p <= p2 );
> + VERIFY( p >= p2 );
> +}
> --
> 2.44.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26
2024-04-10 8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
@ 2024-04-15 18:30 ` Jonathan Wakely
0 siblings, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-15 18:30 UTC (permalink / raw)
To: libstdc++, gcc-patches
Pushed to trunk now.
On Wed, 10 Apr 2024 at 09:53, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> Tested x86_64-linux.
>
> Since this only affects C++26 it seems OK for trunk now.
>
> -- >8 --
>
> This C++26 change was just approved in Tokyo, in P2944R3. It adds
> operator== and operator<=> overloads to std::reference_wrapper.
>
> The operator<=> overloads in the paper cause compilation errors for any
> type without <=> so they're implemented here with deduced return types
> and constrained by a requires clause.
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/refwrap.h (reference_wrapper): Add comparison
> operators as proposed by P2944R3.
> * include/bits/version.def (reference_wrapper): Define.
> * include/bits/version.h: Regenerate.
> * include/std/functional: Enable feature test macro.
> * testsuite/20_util/reference_wrapper/compare.cc: New test.
> ---
> libstdc++-v3/include/bits/refwrap.h | 45 +++++++++
> libstdc++-v3/include/bits/version.def | 8 ++
> libstdc++-v3/include/bits/version.h | 10 ++
> libstdc++-v3/include/std/functional | 1 +
> .../20_util/reference_wrapper/compare.cc | 95 +++++++++++++++++++
> 5 files changed, 159 insertions(+)
> create mode 100644 libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
>
> diff --git a/libstdc++-v3/include/bits/refwrap.h b/libstdc++-v3/include/bits/refwrap.h
> index 2d4338b718f..fd1cc2b63e6 100644
> --- a/libstdc++-v3/include/bits/refwrap.h
> +++ b/libstdc++-v3/include/bits/refwrap.h
> @@ -38,6 +38,10 @@
> #include <bits/invoke.h>
> #include <bits/stl_function.h> // for unary_function and binary_function
>
> +#if __glibcxx_reference_wrapper >= 202403L // >= C++26
> +# include <compare>
> +#endif
> +
> namespace std _GLIBCXX_VISIBILITY(default)
> {
> _GLIBCXX_BEGIN_NAMESPACE_VERSION
> @@ -358,6 +362,47 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type)
> #endif
> return std::__invoke(get(), std::forward<_Args>(__args)...);
> }
> +
> +#if __glibcxx_reference_wrapper >= 202403L // >= C++26
> + // [refwrap.comparisons], comparisons
> + [[nodiscard]]
> + friend constexpr bool
> + operator==(reference_wrapper __x, reference_wrapper __y)
> + requires requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
> + { return __x.get() == __y.get(); }
> +
> + [[nodiscard]]
> + friend constexpr bool
> + operator==(reference_wrapper __x, const _Tp& __y)
> + requires requires { { __x.get() == __y } -> convertible_to<bool>; }
> + { return __x.get() == __y; }
> +
> + [[nodiscard]]
> + friend constexpr bool
> + operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
> + requires (!is_const_v<_Tp>)
> + && requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
> + { return __x.get() == __y.get(); }
> +
> + [[nodiscard]]
> + friend constexpr auto
> + operator<=>(reference_wrapper __x, reference_wrapper<_Tp> __y)
> + requires requires { __detail::__synth3way(__x.get(), __y.get()); }
> + { return __detail::__synth3way(__x.get(), __y.get()); }
> +
> + [[nodiscard]]
> + friend constexpr auto
> + operator<=>(reference_wrapper __x, const _Tp& __y)
> + requires requires { __detail::__synth3way(__x.get(), __y); }
> + { return __detail::__synth3way(__x.get(), __y); }
> +
> + [[nodiscard]]
> + friend constexpr auto
> + operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
> + requires (!is_const_v<_Tp>)
> + && requires { __detail::__synth3way(__x.get(), __y.get()); }
> + { return __detail::__synth3way(__x.get(), __y.get()); }
> +#endif
> };
>
> #if __cpp_deduction_guides
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 5ad44941bff..5c0477fb61e 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1760,6 +1760,14 @@ ftms = {
> };
> };
>
> +ftms = {
> + name = reference_wrapper;
> + values = {
> + v = 202403;
> + cxxmin = 26;
> + };
> +};
> +
> ftms = {
> name = saturation_arithmetic;
> values = {
> diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> index 460a3e0116a..65e708c73fb 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -1963,6 +1963,16 @@
> #endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
> #undef __glibcxx_want_ratio
>
> +#if !defined(__cpp_lib_reference_wrapper)
> +# if (__cplusplus > 202302L)
> +# define __glibcxx_reference_wrapper 202403L
> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_reference_wrapper)
> +# define __cpp_lib_reference_wrapper 202403L
> +# endif
> +# endif
> +#endif /* !defined(__cpp_lib_reference_wrapper) && defined(__glibcxx_want_reference_wrapper) */
> +#undef __glibcxx_want_reference_wrapper
> +
> #if !defined(__cpp_lib_saturation_arithmetic)
> # if (__cplusplus > 202302L)
> # define __glibcxx_saturation_arithmetic 202311L
> diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
> index 766558b3ce0..99364286a72 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -83,6 +83,7 @@
> #define __glibcxx_want_move_only_function
> #define __glibcxx_want_not_fn
> #define __glibcxx_want_ranges
> +#define __glibcxx_want_reference_wrapper
> #define __glibcxx_want_transparent_operators
> #include <bits/version.h>
>
> diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
> new file mode 100644
> index 00000000000..039c9d26496
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
> @@ -0,0 +1,95 @@
> +// { dg-do compile { target c++26 } }
> +
> +
> +#include <functional>
> +
> +#ifndef __cpp_lib_reference_wrapper
> +# error "Feature-test macro for reference_wrapper missing"
> +#elif __cpp_lib_reference_wrapper != 202403
> +# error "Feature-test macro for reference_wrapper has wrong value"
> +#endif
> +
> +// P2944R3 Comparisons for reference_wrapper
> +
> +auto check(int i, std::reference_wrapper<int> r) -> bool {
> + return i == r;
> +}
> +
> +template <class T> using Ref = std::reference_wrapper<T>;
> +
> +template <class T>
> +concept ref_equality_comparable
> += requires (T a, T const ca, Ref<T> r, Ref<T const> cr) {
> + // the usual T is equality-comparable with itself
> + a == a;
> + a == ca;
> + ca == ca;
> +
> + // Ref<T> is equality-comparable with itself
> + r == r;
> + r == cr;
> + cr == cr;
> +
> + // T and Ref<T> are equality-comparable
> + a == r;
> + a == cr;
> + ca == r;
> + ca == cr;
> +};
> +
> +static_assert( ref_equality_comparable<int> );
> +
> +struct A {
> + auto operator==(A const&) const -> bool { return true; }
> +};
> +
> +struct B {
> + friend auto operator==(B const&, B const&) -> bool { return true; }
> +};
> +
> +template <class T>
> +struct C {
> + friend auto operator==(C const&, C const&) -> bool { return true; }
> +};
> +
> +template <class T>
> +struct D { };
> +template <class T>
> +auto operator==(D<T> const&, D<T> const&) -> bool { return true; }
> +
> +static_assert(ref_equality_comparable<int>);
> +static_assert(ref_equality_comparable<A>);
> +static_assert(ref_equality_comparable<B>);
> +static_assert(ref_equality_comparable<C<int>>);
> +static_assert(ref_equality_comparable<D<int>>);
> +#include <string_view>
> +static_assert(ref_equality_comparable<std::string_view>);
> +
> +template <typename T>
> +struct ValArray {
> + friend auto operator==(ValArray const&, ValArray const&) -> ValArray<bool> {
> + return {};
> + }
> +};
> +
> +void f(ValArray<int> v) {
> + // this is valid and has type ValArray<bool>
> + v == v;
> +
> + // this is also valid today and has the same type
> + std::ref(v) == std::ref(v);
> +}
> +
> +struct ComparesAsInt {
> + friend auto operator==(ComparesAsInt, ComparesAsInt) -> int;
> +};
> +
> +auto f(std::reference_wrapper<ComparesAsInt> a,
> + std::reference_wrapper<ComparesAsInt> b) {
> + // today: compiles and returns int
> + // proposed: compiles and returns bool
> + return a == b;
> +}
> +
> +ComparesAsInt& c();
> +static_assert( std::is_same_v<decltype(f(c(), c())), bool> );
> --
> 2.44.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 4/4] libstdc++: Simplify std::variant comparison operators
2024-04-10 8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
2024-04-10 10:45 ` [PATCH 5/4] libstdc++: Rewrite std::variant comparisons without macros Jonathan Wakely
@ 2024-05-07 13:45 ` Jonathan Wakely
1 sibling, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-05-07 13:45 UTC (permalink / raw)
To: libstdc++, gcc-patches
On Wed, 10 Apr 2024 at 09:51, Jonathan Wakely wrote:
>
> Tested x86_64-linux.
>
> This is just a minor clean-up and could wait for stage 1.
Pushed now.
>
> -- >8 --
>
> libstdc++-v3/ChangeLog:
>
> * include/std/variant (_VARIANT_RELATION_FUNCTION_TEMPLATE):
> Simplify.
> ---
> libstdc++-v3/include/std/variant | 20 +++++++++-----------
> 1 file changed, 9 insertions(+), 11 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
> index 5ba6d9d42e3..2be0f0c1db7 100644
> --- a/libstdc++-v3/include/std/variant
> +++ b/libstdc++-v3/include/std/variant
> @@ -1245,7 +1245,7 @@ namespace __variant
> # define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP)
> #endif
>
> -#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP, __NAME) \
> +#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \
> template<typename... _Types> \
> _VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \
> constexpr bool \
> @@ -1262,22 +1262,20 @@ namespace __variant
> { \
> auto& __this_mem = std::get<__rhs_index>(__lhs); \
> __ret = __this_mem __OP __rhs_mem; \
> + return; \
> } \
> - else \
> - __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
> } \
> - else \
> - __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
> + __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
> }, __rhs); \
> return __ret; \
> }
>
> - _VARIANT_RELATION_FUNCTION_TEMPLATE(<, less)
> - _VARIANT_RELATION_FUNCTION_TEMPLATE(<=, less_equal)
> - _VARIANT_RELATION_FUNCTION_TEMPLATE(==, equal)
> - _VARIANT_RELATION_FUNCTION_TEMPLATE(!=, not_equal)
> - _VARIANT_RELATION_FUNCTION_TEMPLATE(>=, greater_equal)
> - _VARIANT_RELATION_FUNCTION_TEMPLATE(>, greater)
> + _VARIANT_RELATION_FUNCTION_TEMPLATE(<)
> + _VARIANT_RELATION_FUNCTION_TEMPLATE(<=)
> + _VARIANT_RELATION_FUNCTION_TEMPLATE(==)
> + _VARIANT_RELATION_FUNCTION_TEMPLATE(!=)
> + _VARIANT_RELATION_FUNCTION_TEMPLATE(>=)
> + _VARIANT_RELATION_FUNCTION_TEMPLATE(>)
>
> #undef _VARIANT_RELATION_FUNCTION_TEMPLATE
>
> --
> 2.44.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2024-05-07 13:45 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-10 8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
2024-04-10 8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
2024-04-15 18:30 ` Jonathan Wakely
2024-04-10 8:45 ` [PATCH 3/4] libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant Jonathan Wakely
2024-04-10 8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
2024-04-10 10:45 ` [PATCH 5/4] libstdc++: Rewrite std::variant comparisons without macros Jonathan Wakely
2024-05-07 13:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
2024-04-15 18:29 ` [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).