"`3236 <https://wg21.link/LWG3236>`__","Random access iterator requirements lack limiting relational operators domain to comparing those from the same range","November 2020","|Nothing To Do|","","|ranges|"
"`3265 <https://wg21.link/LWG3265>`__","``move_iterator``'s conversions are more broken after P1207","November 2020","Fixed by `LWG3435 <https://wg21.link/LWG3435>`__",""
"`3435 <https://wg21.link/LWG3435>`__","``three_way_comparable_with<reverse_iterator<int*>, reverse_iterator<const int*>>``","November 2020","|Complete|","13.0"
-"`3432 <https://wg21.link/LWG3432>`__","Missing requirement for ``comparison_category``","November 2020","","","|spaceship|"
+"`3432 <https://wg21.link/LWG3432>`__","Missing requirement for ``comparison_category``","November 2020","|Complete|","16.0","|spaceship|"
"`3447 <https://wg21.link/LWG3447>`__","Deduction guides for ``take_view`` and ``drop_view`` have different constraints","November 2020","|Complete|","14.0"
"`3450 <https://wg21.link/LWG3450>`__","The const overloads of ``take_while_view::begin/end`` are underconstrained","November 2020","","","|ranges|"
"`3464 <https://wg21.link/LWG3464>`__","``istream::gcount()`` can overflow","November 2020","",""
| `[stacktrace.entry.cmp] <https://wg21.link/stacktrace.entry.cmp>`_,| stacktrace_entry,None,Unassigned,|Not Started|
| `[stacktrace.basic.cmp] <https://wg21.link/stacktrace.basic.cmp>`_,| basic_stacktrace,[alg.three.way],Unassigned,|Not Started|
| `[string.cmp] <https://wg21.link/string.cmp>`_,| basic_string,None,Mark de Wever,|In Progress|
-| `[string.view.comparison] <https://wg21.link/string.view.comparison>`_,| `basic_string_view <https://reviews.llvm.org/D130295>`_,None,Mark de Wever,|In Progress|
+| `[string.view.comparison] <https://wg21.link/string.view.comparison>`_,| `basic_string_view <https://reviews.llvm.org/D130295>`_,None,Mark de Wever,|Complete|
| `[array.syn] <https://wg21.link/array.syn>`_ (`general <https://wg21.link/container.requirements.general#14>`_),| array,[expos.only.func],Unassigned,|Not Started|
| `[deque.syn] <https://wg21.link/deque.syn>`_ (`general <https://wg21.link/container.requirements.general#14>`_),| deque,[expos.only.func],Unassigned,|Not Started|
| `[forward.list.syn] <https://wg21.link/forward.list.syn>`_ (`general <https://wg21.link/container.requirements.general#14>`_),| forward_list,[expos.only.func],Unassigned,|Not Started|
inline constexpr strong_ordering strong_ordering::equivalent(_OrdResult::__equiv);
inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater);
+/// [cmp.categories.pre]/1
+/// The types partial_ordering, weak_ordering, and strong_ordering are
+/// collectively termed the comparison category types.
+template <class _Tp>
+concept __comparison_category = __one_of_v<_Tp, partial_ordering, weak_ordering, strong_ordering>;
+
#endif // _LIBCPP_STD_VER > 17
_LIBCPP_END_NAMESPACE_STD
#include <__config>
#include <__functional/hash.h>
#include <__iterator/iterator_traits.h>
+#include <compare>
#include <cstdint>
#include <cstdio>
#include <cstring>
typedef streamoff off_type;
typedef streampos pos_type;
typedef mbstate_t state_type;
+#if _LIBCPP_STD_VER > 17
+ using comparison_category = strong_ordering;
+#endif
static inline _LIBCPP_CONSTEXPR_AFTER_CXX14
void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
typedef streamoff off_type;
typedef streampos pos_type;
typedef mbstate_t state_type;
+# if _LIBCPP_STD_VER > 17
+ using comparison_category = strong_ordering;
+# endif
static inline _LIBCPP_CONSTEXPR_AFTER_CXX14
void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
typedef streamoff off_type;
typedef u8streampos pos_type;
typedef mbstate_t state_type;
+# if _LIBCPP_STD_VER > 17
+ using comparison_category = strong_ordering;
+# endif
static inline constexpr void assign(char_type& __c1, const char_type& __c2) noexcept
{__c1 = __c2;}
typedef streamoff off_type;
typedef u16streampos pos_type;
typedef mbstate_t state_type;
+#if _LIBCPP_STD_VER > 17
+ using comparison_category = strong_ordering;
+#endif
static inline _LIBCPP_CONSTEXPR_AFTER_CXX14
void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
typedef streamoff off_type;
typedef u32streampos pos_type;
typedef mbstate_t state_type;
+#if _LIBCPP_STD_VER > 17
+ using comparison_category = strong_ordering;
+#endif
static inline _LIBCPP_CONSTEXPR_AFTER_CXX14
void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
string_view synopsis
+#include <compare>
+
namespace std {
// 7.2, Class template basic_string_view
template<class charT, class traits>
constexpr bool operator==(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) noexcept;
- template<class charT, class traits>
+ template<class charT, class traits> // Removed in C++20
constexpr bool operator!=(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) noexcept;
- template<class charT, class traits>
+ template<class charT, class traits> // Removed in C++20
constexpr bool operator< (basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) noexcept;
- template<class charT, class traits>
+ template<class charT, class traits> // Removed in C++20
constexpr bool operator> (basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) noexcept;
- template<class charT, class traits>
+ template<class charT, class traits> // Removed in C++20
constexpr bool operator<=(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) noexcept;
- template<class charT, class traits>
+ template<class charT, class traits> // Removed in C++20
constexpr bool operator>=(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) noexcept;
+ template<class charT, class traits> // Since C++20
+ constexpr see below operator<=>(basic_string_view<charT, traits> x,
+ basic_string_view<charT, traits> y) noexcept;
+
// see below, sufficient additional overloads of comparison functions
// 7.10, Inserters and extractors
return __lhs.compare(__rhs) == 0;
}
+#if _LIBCPP_STD_VER < 20
+// This overload is automatically generated in C++20.
template<class _CharT, class _Traits, int = 2>
_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
bool operator==(typename common_type<basic_string_view<_CharT, _Traits> >::type __lhs,
if ( __lhs.size() != __rhs.size()) return false;
return __lhs.compare(__rhs) == 0;
}
+#endif // _LIBCPP_STD_VER > 17
+
+// operator <=>
+
+#if _LIBCPP_STD_VER > 17
+
+template <class _CharT, class _Traits>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+operator<=>(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) noexcept {
+ if constexpr (requires { typename _Traits::comparison_category; }) {
+ // [string.view]/4
+ static_assert(
+ __comparison_category<typename _Traits::comparison_category>,
+ "return type is not a comparison category type");
+ return static_cast<typename _Traits::comparison_category>(__lhs.compare(__rhs) <=> 0);
+ } else {
+ return static_cast<weak_ordering>(__lhs.compare(__rhs) <=> 0);
+ }
+}
+
+template <class _CharT, class _Traits, int = 1>
+_LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(
+ basic_string_view<_CharT, _Traits> __lhs, common_type_t<basic_string_view<_CharT, _Traits>> __rhs) noexcept {
+ if constexpr (requires { typename _Traits::comparison_category; }) {
+ // [string.view]/4
+ static_assert(
+ __comparison_category<typename _Traits::comparison_category>,
+ "return type is not a comparison category type");
+ return static_cast<typename _Traits::comparison_category>(__lhs.compare(__rhs) <=> 0);
+ } else {
+ return static_cast<weak_ordering>(__lhs.compare(__rhs) <=> 0);
+ }
+}
+#else // _LIBCPP_STD_VER > 17
// operator !=
template<class _CharT, class _Traits>
return __lhs.compare(__rhs) >= 0;
}
+#endif // _LIBCPP_STD_VER > 17
template<class _CharT, class _Traits>
basic_ostream<_CharT, _Traits>&
// typedef streamoff off_type;
// typedef streampos pos_type;
// typedef mbstate_t state_type;
+// using comparison_category = strong_ordering;
#include <string>
#include <type_traits>
static_assert((std::is_same<std::char_traits<char>::off_type, std::streamoff>::value), "");
static_assert((std::is_same<std::char_traits<char>::pos_type, std::streampos>::value), "");
static_assert((std::is_same<std::char_traits<char>::state_type, std::mbstate_t>::value), "");
+#if TEST_STD_VER > 17
+ static_assert(std::is_same_v<std::char_traits<char>::comparison_category, std::strong_ordering>);
+#endif
return 0;
}
// typedef streamoff off_type;
// typedef u16streampos pos_type;
// typedef mbstate_t state_type;
+// using comparison_category = strong_ordering;
#include <string>
#include <type_traits>
static_assert((std::is_same<std::char_traits<char16_t>::off_type, std::streamoff>::value), "");
static_assert((std::is_same<std::char_traits<char16_t>::pos_type, std::u16streampos>::value), "");
static_assert((std::is_same<std::char_traits<char16_t>::state_type, std::mbstate_t>::value), "");
+#if TEST_STD_VER > 17
+ static_assert(std::is_same_v<std::char_traits<char16_t>::comparison_category, std::strong_ordering>);
+#endif
return 0;
}
// typedef streamoff off_type;
// typedef u32streampos pos_type;
// typedef mbstate_t state_type;
+// using comparison_category = strong_ordering;
#include <string>
#include <type_traits>
#include <cstdint>
+#include "test_macros.h"
+
static_assert((std::is_same<std::char_traits<char32_t>::char_type, char32_t>::value), "");
static_assert((std::is_same<std::char_traits<char32_t>::int_type, std::uint_least32_t>::value), "");
static_assert((std::is_same<std::char_traits<char32_t>::off_type, std::streamoff>::value), "");
static_assert((std::is_same<std::char_traits<char32_t>::pos_type, std::u32streampos>::value), "");
static_assert((std::is_same<std::char_traits<char32_t>::state_type, std::mbstate_t>::value), "");
+#if TEST_STD_VER > 17
+static_assert(std::is_same_v<std::char_traits<char32_t>::comparison_category, std::strong_ordering>);
+#endif
// typedef streamoff off_type;
// typedef u8streampos pos_type;
// typedef mbstate_t state_type;
+// using comparison_category = strong_ordering;
#include <string>
#include <type_traits>
static_assert((std::is_same<std::char_traits<char8_t>::off_type, std::streamoff>::value), "");
static_assert((std::is_same<std::char_traits<char8_t>::pos_type, std::u8streampos>::value), "");
static_assert((std::is_same<std::char_traits<char8_t>::state_type, std::mbstate_t>::value), "");
+ static_assert(std::is_same_v<std::char_traits<char8_t>::comparison_category, std::strong_ordering>);
#endif
return 0;
// typedef streamoff off_type;
// typedef streampos pos_type;
// typedef mbstate_t state_type;
+// using comparison_category = strong_ordering;
// UNSUPPORTED: no-wide-characters
static_assert((std::is_same<std::char_traits<wchar_t>::off_type, std::streamoff>::value), "");
static_assert((std::is_same<std::char_traits<wchar_t>::pos_type, std::wstreampos>::value), "");
static_assert((std::is_same<std::char_traits<wchar_t>::state_type, std::mbstate_t>::value), "");
+#if TEST_STD_VER > 17
+ static_assert(std::is_same_v<std::char_traits<wchar_t>::comparison_category, std::strong_ordering>);
+#endif
return 0;
}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Starting with C++20 the spaceship operator was included. This tests
+// comparison in that context, thus doesn't support older language versions.
+// These are tested per operator.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <string_view>
+
+// template<class charT, class traits>
+// constexpr bool operator==(basic_string_view<charT, traits> lhs, basic_string_view<charT, traits> rhs);
+// template<class charT, class traits>
+// constexpr auto operator<=>(basic_string_view<charT, traits> lhs, basic_string_view<charT, traits> rhs);
+// (plus "sufficient additional overloads" to make implicit conversions work as intended)
+
+#include <string_view>
+
+#include <array>
+#include <cassert>
+#include <string>
+
+#include "constexpr_char_traits.h"
+#include "make_string.h"
+#include "test_comparisons.h"
+#include "test_macros.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+// Copied from constexpr_char_traits, but it doesn't have a full implementation.
+// It has a comparison_category used in the tests.
+template <class CharT, class Ordering>
+struct char_traits {
+ using char_type = CharT;
+ using int_type = int;
+ using off_type = std::streamoff;
+ using pos_type = std::streampos;
+ using state_type = std::mbstate_t;
+ using comparison_category = Ordering;
+
+ static constexpr void assign(char_type& __c1, const char_type& __c2) noexcept { __c1 = __c2; }
+ static constexpr bool eq(char_type __c1, char_type __c2) noexcept { return __c1 == __c2; }
+ static constexpr bool lt(char_type __c1, char_type __c2) noexcept { return __c1 < __c2; }
+ static constexpr int compare(const char_type* __s1, const char_type* __s2, size_t __n) {
+ for (; __n; --__n, ++__s1, ++__s2) {
+ if (lt(*__s1, *__s2))
+ return -1;
+ if (lt(*__s2, *__s1))
+ return 1;
+ }
+ return 0;
+ }
+
+ static constexpr size_t length(const char_type* __s);
+ static constexpr const char_type* find(const char_type* __s, size_t __n, const char_type& __a);
+ static constexpr char_type* move(char_type* __s1, const char_type* __s2, size_t __n);
+ static constexpr char_type* copy(char_type* __s1, const char_type* __s2, size_t __n);
+ static constexpr char_type* assign(char_type* __s, size_t __n, char_type __a);
+ static constexpr int_type not_eof(int_type __c) noexcept { return eq_int_type(__c, eof()) ? ~eof() : __c; }
+ static constexpr char_type to_char_type(int_type __c) noexcept { return char_type(__c); }
+ static constexpr int_type to_int_type(char_type __c) noexcept { return int_type(__c); }
+ static constexpr bool eq_int_type(int_type __c1, int_type __c2) noexcept { return __c1 == __c2; }
+ static constexpr int_type eof() noexcept { return int_type(EOF); }
+};
+
+template <class T, class Ordering = std::strong_ordering>
+constexpr void test() {
+ AssertOrderAreNoexcept<T>();
+ AssertOrderReturn<Ordering, T>();
+
+ using CharT = typename T::value_type;
+
+ // sorted values
+ std::array v{
+ SV(""),
+ SV("abc"),
+ SV("abcdef"),
+ };
+
+ // sorted values with embedded NUL character
+ std::array vn{
+ SV("abc"),
+ SV("abc\0"),
+ SV("abc\0def"),
+ };
+ static_assert(v.size() == vn.size());
+
+ for (size_t i = 0; i < v.size(); ++i) {
+ for (size_t j = 0; j < v.size(); ++j) {
+ assert(testOrder(v[i], v[j], i == j ? Ordering::equivalent : i < j ? Ordering::less : Ordering::greater));
+ assert(testOrder(
+ v[i],
+ std::basic_string<CharT>{v[j]},
+ i == j ? Ordering::equivalent
+ : i < j ? Ordering::less
+ : Ordering::greater));
+
+ assert(testOrder(
+ v[i],
+ std::basic_string<CharT>{v[j]}.c_str(),
+ i == j ? Ordering::equivalent
+ : i < j ? Ordering::less
+ : Ordering::greater));
+
+ // NUL test omitted for c-strings since it will fail.
+ assert(testOrder(vn[i], vn[j], i == j ? Ordering::equivalent : i < j ? Ordering::less : Ordering::greater));
+ assert(testOrder(
+ vn[i],
+ std::basic_string<CharT>{vn[j]},
+ i == j ? Ordering::equivalent
+ : i < j ? Ordering::less
+ : Ordering::greater));
+ }
+ }
+}
+
+template <class CharT>
+constexpr void test_all_orderings() {
+ test<std::basic_string_view<CharT>>(); // Strong ordering in its char_traits
+ test<std::basic_string_view<CharT, constexpr_char_traits<CharT>>,
+ std::weak_ordering>(); // No ordering in its char_traits
+ test<std::basic_string_view<CharT, char_traits<CharT, std::weak_ordering>>, std::weak_ordering>();
+ test<std::basic_string_view<CharT, char_traits<CharT, std::partial_ordering>>, std::partial_ordering>();
+}
+
+constexpr bool test_all_types() {
+ test_all_orderings<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_all_orderings<wchar_t>();
+#endif
+ test_all_orderings<char8_t>();
+ test_all_orderings<char16_t>();
+ test_all_orderings<char32_t>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test_all_types();
+ static_assert(test_all_types());
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <string_view>
+
+// template<class charT, class traits>
+// constexpr auto operator<=>(basic_string_view<charT, traits> lhs, basic_string_view<charT, traits> rhs);
+//
+// LWG 3432
+// [string.view]/4
+// Mandates: R denotes a comparison category type ([cmp.categories]).
+
+#include <string_view>
+
+#include "test_macros.h"
+
+template <class CharT, class Ordering>
+struct traits {
+ typedef CharT char_type;
+ typedef int int_type;
+ typedef std::streamoff off_type;
+ typedef std::streampos pos_type;
+ typedef std::mbstate_t state_type;
+ using comparison_category = Ordering;
+
+ static constexpr void assign(char_type&, const char_type&) noexcept;
+ static constexpr bool eq(char_type&, const char_type&) noexcept;
+ static constexpr bool lt(char_type&, const char_type&) noexcept;
+
+ static constexpr int compare(const char_type*, const char_type*, size_t) { return 0; }
+ static constexpr size_t length(const char_type*);
+ static constexpr const char_type* find(const char_type*, size_t, const char_type&);
+ static constexpr char_type* move(char_type*, const char_type*, size_t);
+ static constexpr char_type* copy(char_type*, const char_type*, size_t);
+ static constexpr char_type* assign(char_type*, size_t, char_type);
+
+ static constexpr int_type not_eof(int_type) noexcept;
+
+ static constexpr char_type to_char_type(int_type) noexcept;
+
+ static constexpr int_type to_int_type(char_type) noexcept;
+
+ static constexpr bool eq_int_type(int_type, int_type) noexcept;
+
+ static constexpr int_type eof() noexcept;
+};
+
+template <class CharT, class Ordering, bool Valid>
+void test() {
+ using type = std::basic_string_view<CharT, traits<CharT, Ordering>>;
+ if constexpr (Valid)
+ type{} <=> type{};
+ else
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ // These diagnostics are issued for
+ // - Every invalid ordering
+ // - Every type
+ // expected-error-re@string_view:* 15 {{{{(static_assert|static assertion)}} failed{{.*}}return type is not a comparison category type}}
+
+ // This diagnostic is not issued for Ordering == void.
+ // expected-error@string_view:* 10 {{no matching conversion for static_cast from}}
+#else
+ // One less test run when wchar_t is unavailable.
+ // expected-error-re@string_view:* 12 {{{{(static_assert|static assertion)}} failed{{.*}}return type is not a comparison category type}}
+ // expected-error@string_view:* 8 {{no matching conversion for static_cast from}}
+#endif
+ type{} <=> type{};
+}
+
+template <class CharT>
+void test_all_orders() {
+ test<CharT, std::strong_ordering, true>();
+ test<CharT, std::weak_ordering, true>();
+ test<CharT, std::partial_ordering, true>();
+
+ test<CharT, void, false>();
+ test<CharT, int, false>();
+ test<CharT, float, false>();
+}
+
+void test_all_types() {
+ test_all_orders<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_all_orders<wchar_t>();
+#endif
+ test_all_orders<char8_t>();
+ test_all_orders<char16_t>();
+ test_all_orders<char32_t>();
+}
typedef std::streamoff off_type;
typedef std::streampos pos_type;
typedef std::mbstate_t state_type;
+ // The comparison_category is omitted so the class will have weak_ordering
+ // in C++20. This is intentional.
static TEST_CONSTEXPR_CXX14 void assign(char_type& __c1, const char_type& __c2) TEST_NOEXCEPT
{__c1 = __c2;}
#include <type_traits>
#include <cassert>
+#include <concepts>
#include "test_macros.h"
// Test all six comparison operations for sanity
}
template <class T, class U = T>
-void AssertComparisonsAreNoexcept()
-{
+TEST_CONSTEXPR_CXX14 void AssertComparisonsAreNoexcept() {
ASSERT_NOEXCEPT(std::declval<const T&>() == std::declval<const U&>());
ASSERT_NOEXCEPT(std::declval<const T&>() != std::declval<const U&>());
ASSERT_NOEXCEPT(std::declval<const T&>() < std::declval<const U&>());
}
template <class T, class U = T>
-void AssertComparisonsReturnBool()
-{
+TEST_CONSTEXPR_CXX14 void AssertComparisonsReturnBool() {
ASSERT_SAME_TYPE(decltype(std::declval<const T&>() == std::declval<const U&>()), bool);
ASSERT_SAME_TYPE(decltype(std::declval<const T&>() != std::declval<const U&>()), bool);
ASSERT_SAME_TYPE(decltype(std::declval<const T&>() < std::declval<const U&>()), bool);
ASSERT_SAME_TYPE(decltype(std::declval<const T&>() >= std::declval<const U&>()), bool);
}
-
template <class T, class U = T>
void AssertComparisonsConvertibleToBool()
{
#if TEST_STD_VER > 17
template <class T, class U = T>
-void AssertOrderAreNoexcept() {
- AssertComparisonsAreNoexcept<T, U>();
- ASSERT_NOEXCEPT(std::declval<const T&>() <=> std::declval<const U&>());
+constexpr void AssertOrderAreNoexcept() {
+ AssertComparisonsAreNoexcept<T, U>();
+ ASSERT_NOEXCEPT(std::declval<const T&>() <=> std::declval<const U&>());
}
template <class Order, class T, class U = T>
-void AssertOrderReturn() {
- AssertComparisonsReturnBool<T, U>();
- ASSERT_SAME_TYPE(decltype(std::declval<const T&>() <=> std::declval<const U&>()), Order);
+constexpr void AssertOrderReturn() {
+ AssertComparisonsReturnBool<T, U>();
+ ASSERT_SAME_TYPE(decltype(std::declval<const T&>() <=> std::declval<const U&>()), Order);
}
template <class Order, class T, class U = T>
constexpr bool testOrder(const T& t1, const U& t2, Order order) {
- return (t1 <=> t2 == order) &&
- testComparisons(t1, t2, order == Order::equal || order == Order::equivalent, order == Order::less);
+ bool equal = order == Order::equivalent;
+ if constexpr (std::same_as<Order, std::strong_ordering>)
+ equal |= order == Order::equal;
+
+ bool less = order == Order::less;
+
+ return (t1 <=> t2 == order) && testComparisons(t1, t2, equal, less);
}
template <class T, class Param>