diff --git a/stl/inc/array b/stl/inc/array index d51bc8f54b9..23c892f3a15 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -775,17 +775,41 @@ _CONSTEXPR20 void swap(array<_Ty, _Size>& _Left, array<_Ty, _Size>& _Right) noex template _NODISCARD _CONSTEXPR20 bool operator==(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 return _STD equal(_Left.begin(), _Left.end(), _Right.begin()); +#else // ^^^ workaround / no workaround vvv + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); +#endif // ^^^ no workaround ^^^ } +#if !_HAS_CXX20 template _NODISCARD _CONSTEXPR20 bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr _Synth_three_way_result<_Ty> operator<=>( + const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 + return _STD lexicographical_compare_three_way( + _Left.begin(), _Left.end(), _Right.begin(), _Right.end(), _Synth_three_way{}); +#else // ^^^ workaround / no workaround vvv + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +#endif // ^^^ no workaround ^^^ +} +#else // __cpp_lib_concepts template _NODISCARD _CONSTEXPR20 bool operator<(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); +#else // ^^^ workaround / no workaround vvv + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); +#endif // ^^^ no workaround ^^^ } template @@ -802,6 +826,7 @@ template _NODISCARD _CONSTEXPR20 bool operator>=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts #if _HAS_CXX20 // FUNCTION TEMPLATE to_array diff --git a/stl/inc/deque b/stl/inc/deque index 73a74b7a717..08f1f6fb47d 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -1581,11 +1581,20 @@ _NODISCARD bool operator==(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Al && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -1606,6 +1615,7 @@ template _NODISCARD bool operator>=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts #if _HAS_CXX20 template diff --git a/stl/inc/forward_list b/stl/inc/forward_list index 24227c66c43..c55199de311 100644 --- a/stl/inc/forward_list +++ b/stl/inc/forward_list @@ -814,6 +814,10 @@ public: return {}; } + _Unchecked_const_iterator _Unchecked_end_iter() const noexcept { + return _Unchecked_const_iterator(nullptr, nullptr); + } + iterator _Make_iter(_Nodeptr _Where) const noexcept { return iterator(_Where, _STD addressof(_Mypair._Myval2)); } @@ -1518,17 +1522,29 @@ void swap(forward_list<_Ty, _Alloc>& _Left, forward_list<_Ty, _Alloc>& _Right) n template _NODISCARD bool operator==(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { - return _STD equal(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD equal( + _Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin(), _Right._Unchecked_end_iter()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>( + const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin(), _Right._Unchecked_end_iter()); } template @@ -1545,6 +1561,7 @@ template _NODISCARD bool operator>=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts #if _HAS_CXX20 template diff --git a/stl/inc/list b/stl/inc/list index a223036db12..d6877e32ebd 100644 --- a/stl/inc/list +++ b/stl/inc/list @@ -1807,17 +1807,28 @@ void swap(list<_Ty, _Alloc>& _Left, list<_Ty, _Alloc>& _Right) noexcept /* stren template _NODISCARD bool operator==(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() && _STD equal(_Left.begin(), _Left.end(), _Right.begin()); + return _Left.size() == _Right.size() + && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); } template @@ -1834,6 +1845,7 @@ template _NODISCARD bool operator>=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts #if _HAS_CXX20 template diff --git a/stl/inc/map b/stl/inc/map index f33f3df84f3..5deb485deeb 100644 --- a/stl/inc/map +++ b/stl/inc/map @@ -369,11 +369,21 @@ _NODISCARD bool operator==(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_ && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result> operator<=>( + const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -394,6 +404,7 @@ template _NODISCARD bool operator>=(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts template void swap(map<_Kty, _Ty, _Pr, _Alloc>& _Left, map<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( @@ -557,12 +568,22 @@ _NODISCARD bool operator==( && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result> operator<=>( + const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { @@ -587,6 +608,7 @@ _NODISCARD bool operator>=( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts template void swap(multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/queue b/stl/inc/queue index 8954046397d..8f7ca511ec5 100644 --- a/stl/inc/queue +++ b/stl/inc/queue @@ -54,6 +54,14 @@ _NODISCARD bool operator>=(const queue<_Ty, _Container>& _Left, const queue<_Ty, return _Left.c >= _Right.c; } +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left.c <=> _Right.c; +} +#endif // __cpp_lib_concepts + template class queue { public: @@ -148,6 +156,11 @@ public: friend bool operator> <>(const queue&, const queue&); friend bool operator<= <>(const queue&, const queue&); friend bool operator>= <>(const queue&, const queue&); +#ifdef __cpp_lib_concepts + template + friend compare_three_way_result_t<_Container2> operator<=>( + const queue<_Ty2, _Container2>&, const queue<_Ty2, _Container2>&); +#endif // __cpp_lib_concepts // clang-format on protected: diff --git a/stl/inc/set b/stl/inc/set index a049d279f22..e80f3838e48 100644 --- a/stl/inc/set +++ b/stl/inc/set @@ -180,11 +180,21 @@ _NODISCARD bool operator==(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Kty> operator<=>( + const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -205,6 +215,7 @@ template _NODISCARD bool operator>=(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts template void swap(set<_Kty, _Pr, _Alloc>& _Left, set<_Kty, _Pr, _Alloc>& _Right) noexcept(noexcept(_Left.swap(_Right))) { @@ -352,11 +363,21 @@ _NODISCARD bool operator==(const multiset<_Kty, _Pr, _Alloc>& _Left, const multi && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Kty> operator<=>( + const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -377,6 +398,7 @@ template _NODISCARD bool operator>=(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts template void swap(multiset<_Kty, _Pr, _Alloc>& _Left, multiset<_Kty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/stack b/stl/inc/stack index 55889064e87..f347ec0b57b 100644 --- a/stl/inc/stack +++ b/stl/inc/stack @@ -52,6 +52,14 @@ _NODISCARD bool operator>=(const stack<_Ty, _Container>& _Left, const stack<_Ty, return _Left.c >= _Right.c; } +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left.c <=> _Right.c; +} +#endif // __cpp_lib_concepts + template class stack { public: @@ -138,6 +146,11 @@ public: friend bool operator> <>(const stack&, const stack&); friend bool operator<= <>(const stack&, const stack&); friend bool operator>= <>(const stack&, const stack&); +#ifdef __cpp_lib_concepts + template + friend compare_three_way_result_t<_Container2> operator<=>( + const stack<_Ty2, _Container2>&, const stack<_Ty2, _Container2>&); +#endif // __cpp_lib_concepts // clang-format on protected: diff --git a/stl/inc/unordered_map b/stl/inc/unordered_map index 0a564aaba2e..c5633ff84a4 100644 --- a/stl/inc/unordered_map +++ b/stl/inc/unordered_map @@ -468,11 +468,13 @@ _NODISCARD bool operator==(const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Allo return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE unordered_multimap template , class _Keyeq = equal_to<_Kty>, @@ -758,11 +760,13 @@ _NODISCARD bool operator==(const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/unordered_set b/stl/inc/unordered_set index 1f32080e230..2a7125567b6 100644 --- a/stl/inc/unordered_set +++ b/stl/inc/unordered_set @@ -322,11 +322,13 @@ _NODISCARD bool operator==(const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _ return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE unordered_multiset template , class _Keyeq = equal_to<_Kty>, class _Alloc = allocator<_Kty>> @@ -584,11 +586,13 @@ _NODISCARD bool operator==(const unordered_multiset<_Kty, _Hasher, _Keyeq, _Allo return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_multiset<_Kty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_multiset<_Kty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/utility b/stl/inc/utility index f40b42623e6..73d0c260f5d 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -695,6 +695,37 @@ _NODISCARD constexpr bool cmp_greater_equal(const _Ty1 _Left, const _Ty2 _Right) return !_STD cmp_less(_Left, _Right); } +#ifdef __cpp_lib_concepts +// STRUCT _Synth_three_way +struct _Synth_three_way { + // clang-format off + template + _NODISCARD constexpr auto operator()(const _Ty1& _Left, const _Ty2& _Right) const + requires requires { + { _Left < _Right } -> _Boolean_testable; + { _Right < _Left } -> _Boolean_testable; + } + // clang-format on + { + if constexpr (three_way_comparable_with<_Ty1, _Ty2>) { + return _Left <=> _Right; + } else { + if (_Left < _Right) { + return weak_ordering::less; + } else if (_Right < _Left) { + return weak_ordering::greater; + } else { + return weak_ordering::equivalent; + } + } + } +}; + +// ALIAS TEMPLATE _Synth_three_way_result +template +using _Synth_three_way_result = decltype(_Synth_three_way{}(_STD declval<_Ty1&>(), _STD declval<_Ty2&>())); +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE in_range template _NODISCARD constexpr _Ty _Min_limit() noexcept { // same as (numeric_limits<_Ty>::min)(), less throughput cost diff --git a/stl/inc/vector b/stl/inc/vector index 17902275c58..4f784ca7eb2 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1755,26 +1755,134 @@ template >, vector(_Iter, _Iter, _Alloc = _Alloc()) -> vector<_Iter_value_t<_Iter>, _Alloc>; #endif // _HAS_CXX17 -template -void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { - _Left.swap(_Right); -} +template +class vector; + +using _Vbase = unsigned int; // word type for vector representation +constexpr int _VBITS = 8 * sizeof(_Vbase); // at least CHAR_BITS bits per word template _NODISCARD bool operator==(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() - && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + if (_Left.size() != _Right.size()) { + return false; + } + +#if _HAS_IF_CONSTEXPR + if constexpr (is_same_v<_Ty, bool>) { + return _STD equal( + _Left._Myvec._Unchecked_begin(), _Left._Myvec._Unchecked_end(), _Right._Myvec._Unchecked_begin()); + } else +#endif // _HAS_IF_CONSTEXPR + { + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + } } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 + +#if _HAS_IF_CONSTEXPR +// Optimize vector lexicographical comparisons. + +// There are several endianness/ordering issues to consider here. +// * Machine endianness is irrelevant. (That affects how an unsigned int is stored +// as a sequence of bytes. While all of our supported architectures are little-endian, +// that's irrelevant as long as we avoid reinterpreting unsigned int as a sequence of bytes.) +// * Appending bits to vector eventually appends words to its underlying storage. +// For example, vb[10] is stored within vb._Myvec[0], while vb[100] is stored within vb._Myvec[3]. +// This allows us to translate lexicographical comparisons from theoretical bits to physical words. +// * Unsigned integers are written and compared as big-endian (most significant bit first). +// For example, 0x10u > 0x07u. +// * However, vector packs bits into words as little-endian (least significant bit first). +// For example, vector{false, true, true, true} stores 0b0000'0000'0000'0000'0000'0000'0000'1110u. +// We could bit-reverse words before comparing, but we just need to find the least significant bit that differs. + +template +struct _Vbase_compare_three_way { + _NODISCARD constexpr _Ret operator()(const _Vbase _Left, const _Vbase _Right) const noexcept { + const _Vbase _Differing_bits = _Left ^ _Right; + + if (_Differing_bits == 0) { // improves _Countr_zero codegen below +#ifdef __cpp_lib_concepts + return strong_ordering::equal; +#else // __cpp_lib_concepts + return 0; +#endif // __cpp_lib_concepts + } + + const int _Bit_index = _Countr_zero(_Differing_bits); // number of least significant bits that match + _STL_INTERNAL_CHECK(_Bit_index < _VBITS); // because we return early for equality + + const _Vbase _Mask = _Vbase{1} << _Bit_index; // selects the least significant bit that differs + + // Instead of comparing (_Left & _Mask) to (_Right & _Mask), we know that exactly one side will be zero. +#ifdef __cpp_lib_concepts + return (_Left & _Mask) == 0 ? strong_ordering::less : strong_ordering::greater; +#else // __cpp_lib_concepts + return (_Left & _Mask) == 0 ? -1 : 1; +#endif // __cpp_lib_concepts + } +}; +#endif // _HAS_IF_CONSTEXPR +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>( + const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { + if constexpr (is_same_v<_Ty, bool>) { + // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. + const auto _Min_word_size = (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + const auto _Left_words = _Left._Myvec._Unchecked_begin(); + const auto _Right_words = _Right._Myvec._Unchecked_begin(); + + using _Comp = _Vbase_compare_three_way; + + const strong_ordering _Word_comparison = _STD lexicographical_compare_three_way( + _Left_words, _Left_words + _Min_word_size, _Right_words, _Right_words + _Min_word_size, _Comp{}); + + if (_Word_comparison != 0) { + return _Word_comparison; + } + + return _Left.size() <=> _Right.size(); + } else { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); + } +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare( - _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); +#if _HAS_IF_CONSTEXPR + if constexpr (is_same_v<_Ty, bool>) { + // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. + auto _First = _Left._Myvec._Unchecked_begin(); + auto _Other = _Right._Myvec._Unchecked_begin(); + + const auto _Last = _First + (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + + for (; _First != _Last; ++_First, (void) ++_Other) { + using _Comp = _Vbase_compare_three_way; + const auto _Result = _Comp{}(*_First, *_Other); + + if (_Result < 0) { + return true; + } else if (_Result > 0) { + return false; + } + } + + return _Left.size() < _Right.size(); + } else +#endif // _HAS_IF_CONSTEXPR + { + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + } } template @@ -1791,11 +1899,26 @@ template _NODISCARD bool operator>=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts -// CLASS TEMPLATE vector AND FRIENDS -using _Vbase = unsigned int; // word type for vector representation -constexpr int _VBITS = 8 * sizeof(_Vbase); // at least CHAR_BITS bits per word +template +void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { + _Left.swap(_Right); +} + +#if _HAS_CXX20 +template +typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { + return _Erase_remove(_Cont, _Val); +} + +template +typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& _Cont, _Pr _Pred) { + return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); +} +#endif // _HAS_CXX20 +// CLASS TEMPLATE vector AND FRIENDS template struct _Wrap_alloc { // TRANSITION, ABI compat, preserves symbol names of vector::iterator using _Alloc = _Alloc0; @@ -2843,16 +2966,6 @@ public: } }; -template -_NODISCARD bool operator==(const vector& _Left, const vector& _Right) { - return _Left.size() == _Right.size() && _Left._Myvec == _Right._Myvec; -} - -template -_NODISCARD bool operator!=(const vector& _Left, const vector& _Right) { - return !(_Left == _Right); -} - // STRUCT TEMPLATE SPECIALIZATION hash template struct hash> { @@ -2864,18 +2977,6 @@ struct hash> { } }; -#if _HAS_CXX20 -template -typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { - return _Erase_remove(_Cont, _Val); -} - -template -typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& _Cont, _Pr _Pred) { - return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); -} -#endif // _HAS_CXX20 - #if _HAS_CXX17 namespace pmr { template diff --git a/tests/std/test.lst b/tests/std/test.lst index 8b46ac33bfa..504942ff6d8 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -99,7 +99,7 @@ tests\Dev11_0000000_user_defined_literals tests\Dev11_0019127_singular_iterators tests\Dev11_0091392_string_erase_resize_perf tests\Dev11_0133625_locale0_implib_cpp -tests\Dev11_0135139_vector_bool_equality_perf +tests\Dev11_0135139_vector_bool_comparisons tests\Dev11_0235721_async_and_packaged_task tests\Dev11_0253803_debug_pointer tests\Dev11_0272959_make_signed @@ -296,6 +296,7 @@ tests\P1032R1_miscellaneous_constexpr tests\P1135R6_atomic_flag_test tests\P1165R1_consistently_propagating_stateful_allocators tests\P1423R3_char8_t_remediation +tests\P1614R2_spaceship tests\P1645R1_constexpr_numeric tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions diff --git a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/env.lst b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/env.lst similarity index 100% rename from tests/std/tests/Dev11_0135139_vector_bool_equality_perf/env.lst rename to tests/std/tests/Dev11_0135139_vector_bool_comparisons/env.lst diff --git a/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp new file mode 100644 index 00000000000..9a17e51d2b1 --- /dev/null +++ b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +vector vb_from_str(const char* str) { + vector vb; + + for (; *str != '\0'; ++str) { + assert(*str == '0' || *str == '1'); + + vb.push_back(*str == '1'); + } + + return vb; +} + +enum class Ordering : int { Less = -1, Equal = 0, Greater = 1 }; + +constexpr Ordering Lt = Ordering::Less; +constexpr Ordering Eq = Ordering::Equal; +constexpr Ordering Gt = Ordering::Greater; + +void test_comparison(const char* const left_str, const char* const right_str, const Ordering order) { + const auto left = vb_from_str(left_str); + const auto right = vb_from_str(right_str); + +#ifdef __cpp_lib_concepts + assert((left <=> right) == (static_cast(order) <=> 0)); + assert((right <=> left) == (0 <=> static_cast(order))); +#endif // __cpp_lib_concepts + + switch (order) { + case Lt: + assert(!(left == right)); + assert(!(right == left)); + assert(left < right); + assert(!(right < left)); + break; + case Eq: + assert(left == right); + assert(right == left); + assert(!(left < right)); + assert(!(right < left)); + break; + case Gt: + assert(!(left == right)); + assert(!(right == left)); + assert(!(left < right)); + assert(right < left); + break; + default: + assert(false); + break; + } +} + +int main() { + + { + mt19937 eng(1729); + + uniform_int_distribution dist(0, 1); + + const size_t N = 137; + + vector x(N); + vector y(N); + + for (size_t i = 0; i < N; ++i) { + const bool b = dist(eng) != 0; + + x[i] = b; + y[i] = b; + } + + assert(x == y); + + y.push_back(0); + + assert(x != y); + + y.pop_back(); + + assert(x == y); + + y.push_back(1); + + assert(x != y); + + y.pop_back(); + + assert(x == y); + + x.back().flip(); + + assert(x != y); + + y.back().flip(); + + assert(x == y); + } + + { + // Also test DevDiv#850453 ": Missing emplace methods in std::vector container". + + vector v(47, allocator()); + + v.emplace_back(make_shared(123)); + v.emplace_back(shared_ptr()); + + v.emplace(v.cbegin(), make_shared(3.14)); + v.emplace(v.cbegin(), make_unique(456)); + v.emplace(v.cbegin(), shared_ptr()); + v.emplace(v.cbegin(), unique_ptr()); + v.emplace(v.cbegin(), unique_ptr()); + + + vector correct; + + correct.insert(correct.cend(), 3, false); + correct.insert(correct.cend(), 2, true); + correct.insert(correct.cend(), 47, false); + correct.insert(correct.cend(), 1, true); + correct.insert(correct.cend(), 1, false); + + assert(v == correct); + } + + // Also test GH-1046 optimizing vector spaceship and less-than comparisons. + test_comparison("", "", Eq); // both empty + test_comparison("", "00", Lt); // empty vs. partial word + test_comparison("", "01", Lt); + test_comparison("", "00000000000000000000000000000000", Lt); // empty vs. full word + test_comparison("", "01000000000000000000000000000000", Lt); + test_comparison("", "0000000000000000000000000000000000", Lt); // empty vs. full and partial words + test_comparison("", "0100000000000000000000000000000001", Lt); + + test_comparison("010111010", "010111010", Eq); + test_comparison("010111010", "011110010", Lt); // test that bits are compared in the correct direction + + // same test, after an initial matching word + test_comparison("00001111000011110000111100001111010111010", "00001111000011110000111100001111010111010", Eq); + test_comparison("00001111000011110000111100001111010111010", "00001111000011110000111100001111011110010", Lt); + + test_comparison("00001111", "00001111", Eq); + test_comparison("00001111", "000011110", Lt); // matching prefixes, test size comparison + test_comparison("00001111", "000011111", Lt); + test_comparison("00001111", "00001111000000000000000000000000", Lt); // full word + test_comparison("00001111", "00001111000000000000000000000001", Lt); + test_comparison("00001111", "0000111100000000000000000000000000", Lt); // full and partial words + test_comparison("00001111", "0000111100000000000000000000000001", Lt); + + test_comparison("10", "01111", Gt); // shorter but greater + test_comparison("10", "01111111111111111111111111111111", Gt); // full word + test_comparison("10", "0111111111111111111111111111111111", Gt); // full and partial words +} diff --git a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp deleted file mode 100644 index 4036fe360c6..00000000000 --- a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include -#include -#include -#include - -using namespace std; - -int main() { - - { - mt19937 eng(1729); - - uniform_int_distribution dist(0, 1); - - const size_t N = 137; - - vector x(N); - vector y(N); - - for (size_t i = 0; i < N; ++i) { - const bool b = dist(eng) != 0; - - x[i] = b; - y[i] = b; - } - - assert(x == y); - - y.push_back(0); - - assert(x != y); - - y.pop_back(); - - assert(x == y); - - y.push_back(1); - - assert(x != y); - - y.pop_back(); - - assert(x == y); - - x.back().flip(); - - assert(x != y); - - y.back().flip(); - - assert(x == y); - } - - { - // Also test DevDiv#850453 ": Missing emplace methods in std::vector container". - - vector v(47, allocator()); - - v.emplace_back(make_shared(123)); - v.emplace_back(shared_ptr()); - - v.emplace(v.cbegin(), make_shared(3.14)); - v.emplace(v.cbegin(), make_unique(456)); - v.emplace(v.cbegin(), shared_ptr()); - v.emplace(v.cbegin(), unique_ptr()); - v.emplace(v.cbegin(), unique_ptr()); - - - vector correct; - - correct.insert(correct.cend(), 3, false); - correct.insert(correct.cend(), 2, true); - correct.insert(correct.cend(), 47, false); - correct.insert(correct.cend(), 1, true); - correct.insert(correct.cend(), 1, false); - - assert(v == correct); - } -} diff --git a/tests/std/tests/P1614R2_spaceship/env.lst b/tests/std/tests/P1614R2_spaceship/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P1614R2_spaceship/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp new file mode 100644 index 00000000000..5a215eac23b --- /dev/null +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -0,0 +1,330 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Covers: +// * spaceship for containers + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using PartiallyOrdered = double; + +struct WeaklyOrdered { + [[nodiscard]] constexpr bool operator==(const WeaklyOrdered&) const { + return true; + } + + [[nodiscard]] constexpr std::weak_ordering operator<=>(const WeaklyOrdered&) const { + return std::weak_ordering::equivalent; + } +}; + +using StronglyOrdered = int; + +// Activates synth-three-way in N4861 16.4.2.1 [expos.only.func]/2. +struct SynthOrdered { + int val; + + constexpr SynthOrdered(const int x) : val{x} {} + + [[nodiscard]] constexpr bool operator==(const SynthOrdered& other) const { + return val == other.val; + } + + [[nodiscard]] constexpr bool operator<(const SynthOrdered& other) const { + return val < other.val; + } +}; + +template +inline constexpr bool is_pair = false; +template +inline constexpr bool is_pair> = true; // TRANSITION, std::pair spaceship not yet implemented + +template +void ordered_containers_test(const Container& smaller, const Container& smaller_equal, const Container& larger) { + assert(smaller < larger); + assert(smaller <= larger); + assert(larger > smaller); + assert(larger >= smaller); + assert(smaller == smaller_equal); + assert(smaller != larger); + assert((smaller <=> larger) < 0); + assert((larger <=> smaller) > 0); + assert((smaller <=> smaller_equal) == 0); + + using Elem = typename Container::value_type; + if constexpr (is_pair // TRANSITION, std::pair spaceship not yet implemented + || std::is_same_v) { + static_assert(std::is_same_v larger), std::weak_ordering>); + } else { + static_assert(std::is_same_v larger), std::strong_ordering>); + } +} + +template +void unordered_containers_test( + const Container& something, const Container& something_equal, const Container& different) { + assert(something == something_equal); + assert(something != different); +} + +void ordering_test_cases() { + { // constexpr array + constexpr std::array a0{{2, 8, 9, 1, 9}}; + constexpr std::array a1{{2, 8, 9}}; + constexpr std::array a2{{2, 8, 9, 1, 8}}; + + static_assert((a0 <=> a0) == 0); + static_assert((a1 <=> a1) == 0); + static_assert((a2 <=> a0) < 0); + static_assert((a0 <=> a2) > 0); + } + { // constexpr array SynthOrdered + constexpr std::array a = {10, 20, 30}; + constexpr std::array b = {10, 20, 40}; + + static_assert((a <=> a) == 0); + static_assert((a <=> b) < 0); + static_assert((b <=> a) > 0); + } + { // array + std::array a1 = {100, 100, 100}; + std::array a2 = {100, 100, 100}; + std::array b1 = {200, 200}; + ordered_containers_test(a1, a2, b1); + } + { // array SynthOrdered + std::array a = {10, 20, 30}; + std::array b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // deque + std::deque a1(3, 100); + std::deque a2(3, 100); + std::deque b1(2, 200); + ordered_containers_test(a1, a2, b1); + } + { // deque SynthOrdered + std::deque a = {10, 20, 30}; + std::deque b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // list + std::list a1(3, 100); + std::list a2(3, 100); + std::list b1(2, 200); + ordered_containers_test(a1, a2, b1); + } + { // list SynthOrdered + std::list a = {10, 20, 30}; + std::list b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // vector + std::vector a1(3, 100); + std::vector a2(3, 100); + std::vector b1(2, 200); + ordered_containers_test(a1, a2, b1); + } + { // vector SynthOrdered + std::vector a = {10, 20, 30}; + std::vector b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // vector + std::vector c1 = {false, true, false}; + std::vector c2 = {false, true, false}; + std::vector d1 = {true, false}; + ordered_containers_test(c1, c2, d1); + } + { // forward_list + std::forward_list a1(3, 100); + std::forward_list a2(3, 100); + std::forward_list b1(2, 200); + ordered_containers_test(a1, a2, b1); + } + { // forward_list SynthOrdered + std::forward_list a = {10, 20, 30}; + std::forward_list b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // map + std::map a1; + a1["hi"] = 1; + a1["hola"] = 2; + std::map a2; + a2["hi"] = 1; + a2["hola"] = 2; + std::map b1; + b1["zoe"] = 3; + b1["koala"] = 4; + ordered_containers_test(a1, a2, b1); + } + { // map SynthOrdered + std::map a = {{10, 'z'}, {20, 'z'}, {30, 'z'}}; + std::map b = {{10, 'z'}, {20, 'z'}, {40, 'z'}}; + ordered_containers_test(a, a, b); + } + { // multimap + std::multimap a1 = {{'a', 1}, {'b', 2}, {'a', 3}}; + std::multimap a2 = {{'a', 1}, {'a', 3}, {'b', 2}}; + std::multimap b1 = {{'z', 4}, {'y', 90}, {'z', 12}}; + ordered_containers_test(a1, a2, b1); + } + { // multimap SynthOrdered + std::multimap a = {{10, 'z'}, {20, 'z'}, {30, 'z'}}; + std::multimap b = {{10, 'z'}, {20, 'z'}, {40, 'z'}}; + ordered_containers_test(a, a, b); + } + { // set + std::set a1; + a1.insert(10); + a1.insert(20); + + std::set a2; + a2.insert(10); + a2.insert(20); + + std::set b1; + b1.insert(30); + b1.insert(40); + ordered_containers_test(a1, a2, b1); + } + { // set SynthOrdered + std::set a = {10, 20, 30}; + std::set b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // multiset + std::multiset a1; + a1.insert(10); + a1.insert(10); + a1.insert(20); + + std::multiset a2; + a2.insert(10); + a2.insert(20); + a2.insert(10); + + std::multiset b1; + b1.insert(30); + b1.insert(40); + b1.insert(40); + ordered_containers_test(a1, a2, b1); + } + { // multiset SynthOrdered + std::multiset a = {10, 20, 30}; + std::multiset b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // unordered_map + using stringmap = std::unordered_map; + stringmap a = {{"cat", "tabby"}, {"dog", "poodle"}, {"bear", "grizzly"}}; + stringmap b = {{"dog", "poodle"}, {"bear", "grizzly"}, {"cat", "tabby"}}; + stringmap c = {{"cat", "siamese"}, {"dog", "lab"}, {"bear", "polar"}}; + unordered_containers_test(a, b, c); + } + { // unordered_multimap + using stringmap = std::unordered_multimap; + stringmap a = {{"cat", "tabby"}, {"dog", "poodle"}, {"cat", "siamese"}, {"dog", "poodle"}}; + stringmap b = {{"dog", "poodle"}, {"cat", "siamese"}, {"cat", "tabby"}, {"dog", "poodle"}}; + stringmap c = {{"cat", "siamese"}, {"dog", "lab"}, {"bear", "polar"}}; + unordered_containers_test(a, b, c); + } + { // unordered_set + std::unordered_set a = {"cat", "dog", "bear"}; + std::unordered_set b = {"bear", "cat", "dog"}; + std::unordered_set c = {"mouse", "cat", "bear", "dog"}; + unordered_containers_test(a, b, c); + } + { // unordered_multiset + std::unordered_multiset a = {"cat", "dog", "cat"}; + std::unordered_multiset b = {"cat", "cat", "dog"}; + std::unordered_multiset c = {"mouse", "cat", "bear", "dog"}; + unordered_containers_test(a, b, c); + } + { // queue + std::deque deq1(3, 100); + std::deque deq2(2, 200); + std::queue a(deq1); + std::queue b(deq1); + std::queue c(deq2); + ordered_containers_test(a, b, c); + } + { // queue SynthOrdered + std::queue a{std::deque{10, 20, 30}}; + std::queue b{std::deque{10, 20, 40}}; + ordered_containers_test(a, a, b); + } + { // stack + std::stack a; + a.push(2); + a.push(2); + std::stack b; + b.push(2); + b.push(2); + std::stack c; + c.push(3); + c.push(3); + ordered_containers_test(a, b, c); + } + { // stack SynthOrdered + std::stack a{std::deque{10, 20, 30}}; + std::stack b{std::deque{10, 20, 40}}; + ordered_containers_test(a, a, b); + } +} + +template +using SpaceshipType = decltype(std::declval() <=> std::declval()); + +template +void test_element_ordering() { + if constexpr (!std::is_same_v) { // SynthOrdered inherently doesn't support <=> directly + static_assert(std::is_same_v, Ordering>); + } + + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + + // TRANSITION, std::pair spaceship not yet implemented + static_assert(std::is_same_v>, std::weak_ordering>); + static_assert(std::is_same_v>, std::weak_ordering>); + + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); +} + +int main() { + ordering_test_cases(); + + test_element_ordering(); + test_element_ordering(); + test_element_ordering(); + test_element_ordering(); +}