diff --git a/stl/inc/array b/stl/inc/array index d51bc8f54b9..6f8e8b722cb 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -111,6 +111,11 @@ public: return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _Array_const_iterator& _Right) const noexcept { + return _Ptr <=> _Right._Ptr; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD _CONSTEXPR17 bool operator!=(const _Array_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -130,6 +135,7 @@ public: _NODISCARD _CONSTEXPR17 bool operator>=(const _Array_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 using _Prevent_inheriting_unwrap = _Array_const_iterator; @@ -235,6 +241,12 @@ private: return _Idx == _Right._Idx; } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _Array_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Idx <=> _Right._Idx; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD _CONSTEXPR17 bool operator!=(const _Array_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -255,6 +267,7 @@ private: _NODISCARD _CONSTEXPR17 bool operator>=(const _Array_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR17 void _Compat(const _Array_const_iterator& _Right) const noexcept { // test for compatible iterator pair _STL_VERIFY(_Ptr == _Right._Ptr, "array iterators incompatible"); @@ -775,17 +788,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) { +_NODISCARD 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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv 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 +839,7 @@ template _NODISCARD _CONSTEXPR20 bool operator>=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 // FUNCTION TEMPLATE to_array diff --git a/stl/inc/bitset b/stl/inc/bitset index 8d21a2676ce..ff75bdd526b 100644 --- a/stl/inc/bitset +++ b/stl/inc/bitset @@ -370,9 +370,11 @@ public: return _CSTD memcmp(&_Array[0], &_Right._Array[0], sizeof(_Array)) == 0; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const bitset& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool test(size_t _Pos) const { if (_Bits <= _Pos) { diff --git a/stl/inc/charconv b/stl/inc/charconv index 9ab5d01ec81..bdb31a1b857 100644 --- a/stl/inc/charconv +++ b/stl/inc/charconv @@ -209,6 +209,9 @@ to_chars_result to_chars(char* _First, char* _Last, bool _Value, int _Base = 10) struct from_chars_result { const char* ptr; errc ec; +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const from_chars_result&, const from_chars_result&) = default; +#endif // _HAS_CXX20 }; // FUNCTION from_chars (STRING TO INTEGER) diff --git a/stl/inc/compare b/stl/inc/compare index 46bdfd0371f..ab47a879606 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -342,6 +342,35 @@ struct compare_three_way { }; // clang-format on +// 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&>())); + // Note: The following CPOs are passing arguments as lvalues; see GH-1374. // CUSTOMIZATION POINT OBJECT strong_order diff --git a/stl/inc/complex b/stl/inc/complex index e0aff1ed4ee..5e935cf52d0 100644 --- a/stl/inc/complex +++ b/stl/inc/complex @@ -1514,12 +1514,15 @@ _NODISCARD constexpr bool operator==(const complex<_Ty>& _Left, const _Ty& _Righ return _Left.real() == _Right && _Left.imag() == 0; } +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator==(const _Ty& _Left, const complex<_Ty>& _Right) { return _Left == _Right.real() && 0 == _Right.imag(); } +#endif // !_HAS_CXX20 // FUNCTION TEMPLATE operator!= +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const complex<_Ty>& _Left, const complex<_Ty>& _Right) { return !(_Left == _Right); @@ -1534,6 +1537,7 @@ template _NODISCARD constexpr bool operator!=(const _Ty& _Left, const complex<_Ty>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // FUNCTION TEMPLATE imag template diff --git a/stl/inc/deque b/stl/inc/deque index cb0f3258fd3..9018290d9c6 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -107,6 +107,11 @@ public: return _Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _Deque_unchecked_const_iterator& _Right) const noexcept { + return _Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Deque_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -126,6 +131,7 @@ public: _NODISCARD bool operator>=(const _Deque_unchecked_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 const _Container_base12* _Getcont() const noexcept { // get container pointer return _Mycont; @@ -345,6 +351,12 @@ public: return this->_Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _Deque_const_iterator& _Right) const noexcept { + _Compat(_Right); + return this->_Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Deque_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -365,6 +377,7 @@ public: _NODISCARD bool operator>=(const _Deque_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 void _Compat(const _Deque_const_iterator& _Right) const noexcept { // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL == 0 @@ -1582,11 +1595,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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -1607,6 +1629,7 @@ template _NODISCARD bool operator>=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/filesystem b/stl/inc/filesystem index c0444a29bd9..05341d65c4c 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -26,6 +26,10 @@ #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -1399,6 +1403,11 @@ namespace filesystem { return _Left.compare(_Right) == 0; } +#if _HAS_CXX20 + _NODISCARD friend strong_ordering operator<=>(const path& _Left, const path& _Right) noexcept { + return _Left.compare(_Right) <=> 0; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD friend bool operator!=(const path& _Left, const path& _Right) noexcept { return _Left.compare(_Right) != 0; } @@ -1418,6 +1427,7 @@ namespace filesystem { _NODISCARD friend bool operator>=(const path& _Left, const path& _Right) noexcept { return _Left.compare(_Right) >= 0; } +#endif // !_HAS_CXX20 _NODISCARD friend path operator/(const path& _Left, const path& _Right) { // append a pair of paths together return path(_Left) /= _Right; @@ -1593,9 +1603,11 @@ namespace filesystem { return _Lhs._Position == _Rhs._Position; } +#if !_HAS_CXX20 _NODISCARD friend bool operator!=(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) { return _Lhs._Position != _Rhs._Position; } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL != 0 friend void _Verify_range(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) { @@ -1967,6 +1979,12 @@ namespace filesystem { return _Myperms; } +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const file_status& _Lhs, const file_status& _Rhs) noexcept { + return _Lhs._Myftype == _Rhs._Myftype && _Lhs._Myperms == _Rhs._Myperms; + } +#endif // _HAS_CXX20 + void _Refresh(const __std_win_error _Error, const __std_fs_stats& _Stats) noexcept { if (_Error == __std_win_error::_Success) { const auto _Attrs = _Stats._Attributes; @@ -2445,18 +2463,23 @@ namespace filesystem { return _Result._Status; } - _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept { - return _Path < _Rhs._Path; - } - _NODISCARD bool operator==(const directory_entry& _Rhs) const noexcept { return _Path == _Rhs._Path; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const directory_entry& _Rhs) const noexcept { + return _Path <=> _Rhs._Path; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const directory_entry& _Rhs) const noexcept { return _Path != _Rhs._Path; } + _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept { + return _Path < _Rhs._Path; + } + _NODISCARD bool operator<=(const directory_entry& _Rhs) const noexcept { return _Path <= _Rhs._Path; } @@ -2468,6 +2491,7 @@ namespace filesystem { _NODISCARD bool operator>=(const directory_entry& _Rhs) const noexcept { return _Path >= _Rhs._Path; } +#endif // !_HAS_CXX20 // [fs.dir.entry.io], inserter template @@ -2706,9 +2730,11 @@ namespace filesystem { return _Impl == _Rhs._Impl; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const directory_iterator& _Rhs) const noexcept /* strengthened */ { return _Impl != _Rhs._Impl; } +#endif // !_HAS_CXX20 _Directory_entry_proxy operator++(int) { _Directory_entry_proxy _Proxy(**this); @@ -2955,9 +2981,11 @@ namespace filesystem { return _Impl == _Rhs._Impl; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const recursive_directory_iterator& _Rhs) const noexcept { return _Impl != _Rhs._Impl; } +#endif // !_HAS_CXX20 _Directory_entry_proxy operator++(int) { _Directory_entry_proxy _Proxy(**this); @@ -3649,6 +3677,10 @@ namespace filesystem { uintmax_t capacity; uintmax_t free; uintmax_t available; + +#if _HAS_CXX20 + _NODISCARD friend constexpr bool operator==(const space_info&, const space_info&) noexcept = default; +#endif // _HAS_CXX20 }; _NODISCARD inline space_info space(const path& _Target) { diff --git a/stl/inc/forward_list b/stl/inc/forward_list index 986f9e3a984..6505741e507 100644 --- a/stl/inc/forward_list +++ b/stl/inc/forward_list @@ -63,17 +63,21 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Flist_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool operator==(_Default_sentinel) const noexcept { return _Ptr == nullptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(_Default_sentinel) const noexcept { return _Ptr != nullptr; } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -161,9 +165,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Flist_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _Flist_const_iterator& _First, const _Flist_const_iterator& _Last) noexcept { @@ -814,6 +820,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 +1528,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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv 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 +1567,7 @@ template _NODISCARD bool operator>=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/functional b/stl/inc/functional index 4a46439d871..a8aee539270 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1281,6 +1281,7 @@ _NODISCARD bool operator==(const function<_Fty>& _Other, nullptr_t) noexcept { return !_Other; } +#if !_HAS_CXX20 template _NODISCARD bool operator==(nullptr_t, const function<_Fty>& _Other) noexcept { return !_Other; @@ -1295,6 +1296,7 @@ template _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept { return static_cast(_Other); } +#endif // !_HAS_CXX20 // PLACEHOLDERS template diff --git a/stl/inc/iterator b/stl/inc/iterator index ca74d58917c..6fd2233e372 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -302,11 +302,13 @@ _NODISCARD bool operator==(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _ return _Left._Equal(_Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Left, const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Right) noexcept /* strengthened */ { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE ostream_iterator template > @@ -488,11 +490,13 @@ _NODISCARD bool operator==( return _Left.equal(_Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const istreambuf_iterator<_Elem, _Traits>& _Left, const istreambuf_iterator<_Elem, _Traits>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE ostreambuf_iterator template @@ -1492,6 +1496,13 @@ public: return _Myindex == _Right._Myindex; } +#if _HAS_CXX20 + _NODISCARD constexpr _STD strong_ordering operator<=>(const checked_array_iterator& _Right) const noexcept { + _STL_VERIFY(_Myarray == _Right._Myarray && _Mysize == _Right._Mysize, + "cannot compare incompatible checked_array_iterators"); + return _Myindex <=> _Right._Myindex; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const checked_array_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1513,6 +1524,7 @@ public: _NODISCARD constexpr bool operator>=(const checked_array_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 friend constexpr void _Verify_range( const checked_array_iterator& _First, const checked_array_iterator& _Last) noexcept { @@ -1646,6 +1658,11 @@ public: return _Myptr == _Right._Myptr; } +#if _HAS_CXX20 + _NODISCARD constexpr _STD strong_ordering operator<=>(const unchecked_array_iterator& _Right) const noexcept { + return _Myptr <=> _Right._Myptr; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const unchecked_array_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1665,6 +1682,7 @@ public: _NODISCARD constexpr bool operator>=(const unchecked_array_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL != 0 friend constexpr void _Verify_range( diff --git a/stl/inc/list b/stl/inc/list index dca51000ff7..58f93ff9bb6 100644 --- a/stl/inc/list +++ b/stl/inc/list @@ -74,9 +74,11 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _List_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -199,9 +201,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _List_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _List_const_iterator& _First, const _List_const_iterator& _Last) noexcept { @@ -1803,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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv 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 @@ -1830,6 +1845,7 @@ template _NODISCARD bool operator>=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/map b/stl/inc/map index 078249a414a..1c66bbcd478 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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv 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 // ^^^ !defined(__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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv 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 // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/memory b/stl/inc/memory index ae479ece7bb..8f84f1bc861 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -1795,6 +1795,13 @@ _NODISCARD bool operator==(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2> return _Left.get() == _Right.get(); } +#if _HAS_CXX20 +template +_NODISCARD strong_ordering operator<=>(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { + return _Left.get() <=> _Right.get(); +} + +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { return _Left.get() != _Right.get(); @@ -1819,12 +1826,20 @@ template _NODISCARD bool operator<=(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { return _Left.get() <= _Right.get(); } +#endif // ^^^ !_HAS_CXX20 ^^^ template _NODISCARD bool operator==(const shared_ptr<_Ty>& _Left, nullptr_t) noexcept { return _Left.get() == nullptr; } +#if _HAS_CXX20 +template +_NODISCARD strong_ordering operator<=>(const shared_ptr<_Ty>& _Left, nullptr_t) noexcept { + return _Left.get() <=> static_cast::element_type*>(nullptr); +} + +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==(nullptr_t, const shared_ptr<_Ty>& _Right) noexcept { return nullptr == _Right.get(); @@ -1879,6 +1894,7 @@ template _NODISCARD bool operator<=(nullptr_t, const shared_ptr<_Ty>& _Right) noexcept { return static_cast::element_type*>(nullptr) <= _Right.get(); } +#endif // ^^^ !_HAS_CXX20 ^^^ template basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Out, const shared_ptr<_Ty>& _Px) { @@ -3419,10 +3435,12 @@ _NODISCARD bool operator==(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr return _Left.get() == _Right.get(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD bool operator<(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { @@ -3447,11 +3465,25 @@ _NODISCARD bool operator<=(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr return !(_Right < _Left); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires three_way_comparable_with::pointer, + typename unique_ptr<_Ty2, _Dx2>::pointer> +_NODISCARD compare_three_way_result_t::pointer, + typename unique_ptr<_Ty2, _Dx2>::pointer> + operator<=>(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { + // clang-format on + return _Left.get() <=> _Right.get(); +} +#endif // __cpp_lib_concepts + template _NODISCARD bool operator==(const unique_ptr<_Ty, _Dx>& _Left, nullptr_t) noexcept { return !_Left; } +#if !_HAS_CXX20 template _NODISCARD bool operator==(nullptr_t, const unique_ptr<_Ty, _Dx>& _Right) noexcept { return !_Right; @@ -3466,6 +3498,7 @@ template _NODISCARD bool operator!=(nullptr_t _Left, const unique_ptr<_Ty, _Dx>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD bool operator<(const unique_ptr<_Ty, _Dx>& _Left, nullptr_t _Right) { @@ -3509,6 +3542,17 @@ _NODISCARD bool operator<=(nullptr_t _Left, const unique_ptr<_Ty, _Dx>& _Right) return !(_Right < _Left); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires three_way_comparable::pointer> +_NODISCARD compare_three_way_result_t::pointer> operator<=>( + const unique_ptr<_Ty, _Dx>& _Left, nullptr_t) { + // clang-format on + return _Left.get() <=> static_cast::pointer>(nullptr); +} +#endif // __cpp_lib_concepts + template struct _Can_stream_unique_ptr : false_type {}; template diff --git a/stl/inc/optional b/stl/inc/optional index 04e36c0c10b..81d2639a164 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -11,6 +11,9 @@ #if !_HAS_CXX17 #pragma message("The contents of are available only with C++17 or later.") #else // ^^^ !_HAS_CXX17 / _HAS_CXX17 vvv +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #include #include #include @@ -480,11 +483,30 @@ _NODISCARD constexpr bool operator>=(const optional<_Ty1>& _Left, const optional return !_Right.has_value() || (_Left.has_value() && *_Left >= *_Right); } +#ifdef __cpp_lib_concepts +template _Ty2> +_NODISCARD constexpr compare_three_way_result_t<_Ty1, _Ty2> operator<=>( + const optional<_Ty1>& _Left, const optional<_Ty2>& _Right) { + if (_Left && _Right) { + return *_Left <=> *_Right; + } + + return _Left.has_value() <=> _Right.has_value(); +} +#endif // __cpp_lib_concepts + // COMPARISONS WITH nullopt [optional.nullops] template _NODISCARD constexpr bool operator==(const optional<_Ty>& _Left, nullopt_t) noexcept { return !_Left.has_value(); } + +#if _HAS_CXX20 +template +_NODISCARD constexpr strong_ordering operator<=>(const optional<_Ty>& _Left, nullopt_t) noexcept { + return _Left.has_value() <=> false; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD constexpr bool operator==(nullopt_t, const optional<_Ty>& _Right) noexcept { return !_Right.has_value(); @@ -534,6 +556,7 @@ template _NODISCARD constexpr bool operator>=(nullopt_t, const optional<_Ty>& _Right) noexcept { return !_Right.has_value(); } +#endif // !_HAS_CXX20 // COMPARISONS WITH T [optional.comp_with_t] template @@ -567,6 +590,7 @@ template = _NODISCARD constexpr bool operator==(const optional<_Ty1>& _Left, const _Ty2& _Right) { return _Left ? *_Left == _Right : false; } + template = 0> _NODISCARD constexpr bool operator==(const _Ty1& _Left, const optional<_Ty2>& _Right) { return _Right ? _Left == *_Right : false; @@ -617,6 +641,22 @@ _NODISCARD constexpr bool operator>=(const _Ty1& _Left, const optional<_Ty2>& _R return _Right ? _Left >= *_Right : true; } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires (!_Is_specialization_v<_Ty2, optional>) // TRANSITION, GH-1674 + && three_way_comparable_with<_Ty1, _Ty2> +_NODISCARD constexpr compare_three_way_result_t<_Ty1, _Ty2> + operator<=>(const optional<_Ty1>& _Left, const _Ty2& _Right) { + // clang-format on + if (_Left) { + return *_Left <=> _Right; + } + + return strong_ordering::less; +} +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE swap [optional.specalg] template && is_swappable_v<_Ty>, int> = 0> void swap(optional<_Ty>& _Left, optional<_Ty>& _Right) noexcept(noexcept(_Left.swap(_Right))) { 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/ranges b/stl/inc/ranges index 1720c95babd..8c386052101 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1548,7 +1548,7 @@ namespace ranges { } // clang-format off - template + template requires sentinel_for<_Base_sentinel, _Base_iterator<_OtherConst>> _NODISCARD friend constexpr bool operator==( const _Counted_iter<_OtherConst>& _Left, const _Sentinel& _Right) { diff --git a/stl/inc/regex b/stl/inc/regex index 330944b8a28..2f2949e2683 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -593,6 +593,18 @@ bool _Is_word(_Elem _Ch) { return _UCh <= static_cast<_UElem>('z') && _Is_word(static_cast(_UCh)); } +#if _HAS_CXX20 +template +struct _Get_member_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_member_comparison_category<_Ty, void_t> { + using type = typename _Ty::comparison_category; +}; +#endif // _HAS_CXX20 + // CLASS TEMPLATE sub_match template class sub_match : public pair<_BidIt, _BidIt> { // class to hold contents of a capture group @@ -607,6 +619,10 @@ public: // Note that _Size_type should always be std::size_t using _Size_type = typename string_type::size_type; +#if _HAS_CXX20 + using _Comparison_category = typename _Get_member_comparison_category<_Traits>::type; +#endif // _HAS_CXX20 + constexpr sub_match() : _Mybase(), matched(false) {} bool matched; @@ -709,6 +725,12 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const sub_match<_BidI return _Left._Match_equal(_Right); } +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left == _Right); @@ -733,9 +755,21 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND NTBS template +_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return _Left._Match_equal(_Right); +} + +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { return _Right._Match_equal(_Left); } @@ -765,11 +799,6 @@ _NODISCARD bool operator>=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_ return !(_Left < _Right); } -template -_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return _Left._Match_equal(_Right); -} - template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return !(_Left == _Right); @@ -794,9 +823,22 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND ELEMENT template +_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return _Left._Match_equal(_STD addressof(_Right), 1); +} + +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return static_cast::_Comparison_category>( + _Left._Compare(_STD addressof(_Right), 1) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return _Right._Match_equal(_STD addressof(_Left), 1); } @@ -826,11 +868,6 @@ _NODISCARD bool operator>=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_ return !(_Left < _Right); } -template -_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return _Left._Match_equal(_STD addressof(_Right), 1); -} - template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { return !(_Left == _Right); @@ -855,6 +892,7 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND string template @@ -863,71 +901,79 @@ _NODISCARD bool operator==( return _Left._Match_equal(_Right.data(), _Right.size()); } +#if _HAS_CXX20 template -_NODISCARD bool operator!=( +_NODISCARD auto operator<=>( const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template +_NODISCARD bool operator==( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return _Right._Match_equal(_Left.data(), _Left.size()); +} + +template +_NODISCARD bool operator!=( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left == _Right); } template _NODISCARD bool operator<( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return _Left._Less(_Right.data(), _Right.size()); + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return _Right._Greater(_Left.data(), _Left.size()); } template _NODISCARD bool operator>( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return _Right < _Left; } template _NODISCARD bool operator<=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return !(_Right < _Left); } template _NODISCARD bool operator>=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return !(_Left < _Right); -} - -template -_NODISCARD bool operator==( const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return _Right._Match_equal(_Left.data(), _Left.size()); + return !(_Left < _Right); } template _NODISCARD bool operator!=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Left == _Right); } template _NODISCARD bool operator<( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return _Right._Greater(_Left.data(), _Left.size()); + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return _Left._Less(_Right.data(), _Right.size()); } template _NODISCARD bool operator>( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return _Right < _Left; } template _NODISCARD bool operator<=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Right < _Left); } template _NODISCARD bool operator>=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // INSERT sub_match IN STREAM template @@ -1135,10 +1181,12 @@ _NODISCARD bool operator==(const match_results<_BidIt, _Alloc>& _Left, const mat } } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const match_results<_BidIt, _Alloc>& _Left, const match_results<_BidIt, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // NFA PROPERTIES const unsigned int _BRE_MAX_GRP = 9U; @@ -2429,9 +2477,11 @@ public: && _MyVal._At(0) == _Right._MyVal._At(0); } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const regex_iterator& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD const value_type& operator*() const { #if _ITERATOR_DEBUG_LEVEL != 0 @@ -2612,9 +2662,11 @@ public: return *_Res == *_Right._Res && _Pos == _Right._Pos && _Subs == _Right._Subs; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const regex_token_iterator& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD const value_type& operator*() const { #if _ITERATOR_DEBUG_LEVEL != 0 diff --git a/stl/inc/scoped_allocator b/stl/inc/scoped_allocator index 76f95f24f36..e1defb79aab 100644 --- a/stl/inc/scoped_allocator +++ b/stl/inc/scoped_allocator @@ -259,12 +259,14 @@ _NODISCARD bool operator==(const scoped_allocator_adaptor<_Outer1>& _Left, return _Left.outer_allocator() == _Right.outer_allocator(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const scoped_allocator_adaptor<_Outer1, _Inner...>& _Left, const scoped_allocator_adaptor<_Outer2, _Inner...>& _Right) noexcept { // compare scoped_allocator_adaptors for equality return !(_Left == _Right); } +#endif // !_HAS_CXX20 _STD_END #pragma pop_macro("new") diff --git a/stl/inc/set b/stl/inc/set index 409f0b7b22a..f45bd81991d 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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv 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 // ^^^ !defined(__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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv 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 // ^^^ !defined(__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/system_error b/stl/inc/system_error index 42b03990542..a9b527db251 100644 --- a/stl/inc/system_error +++ b/stl/inc/system_error @@ -16,7 +16,11 @@ #include #ifndef _M_CEE_PURE #include -#endif +#endif // _M_CEE_PURE + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -90,13 +94,22 @@ public: return _Addr == _Right._Addr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const error_category& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD strong_ordering operator<=>(const error_category& _Right) const noexcept { + return compare_three_way{}(_Addr, _Right._Addr); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv _NODISCARD bool operator<(const error_category& _Right) const noexcept { return _Addr < _Right._Addr; } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ error_category(const error_category&) = delete; error_category& operator=(const error_category&) = delete; @@ -173,6 +186,21 @@ public: return _System_error_equal(_Left, _Right); } +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD friend strong_ordering operator<=>(const error_code& _Left, const error_code& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + _NODISCARD friend bool operator<(const error_code& _Left, const error_code& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); + } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ +#if !_HAS_CXX20 _NODISCARD friend bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { return _System_error_equal(_Right, _Left); } @@ -188,11 +216,7 @@ public: _NODISCARD friend bool operator!=(const error_condition& _Left, const error_code& _Right) noexcept { return !_System_error_equal(_Right, _Left); } - - _NODISCARD friend bool operator<(const error_code& _Left, const error_code& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); - } +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS private: @@ -249,22 +273,36 @@ public: return _Left.category() == _Right.category() && _Left.value() == _Right.value(); } - _NODISCARD friend bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { - return !(_Left == _Right); +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD friend strong_ordering operator<=>( + const error_condition& _Left, const error_condition& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); } - +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv _NODISCARD friend bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { return _Left.category() < _Right.category() || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ +#if !_HAS_CXX20 + _NODISCARD friend bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { + return !(_Left == _Right); + } +#endif // !_HAS_CXX20 // We grant friendship to the operators from error_code here to allow is_error_code_enum_v but not // is_error_condition_enum_v enums to be compared directly with error_condition; for example: // io_errc::stream == make_error_condition(errc::out_of_memory) friend bool operator==(const error_code& _Left, const error_condition& _Right) noexcept; +#if !_HAS_CXX20 friend bool operator==(const error_condition& _Left, const error_code& _Right) noexcept; friend bool operator!=(const error_code& _Left, const error_condition& _Right) noexcept; friend bool operator!=(const error_condition& _Left, const error_code& _Right) noexcept; +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS private: @@ -285,14 +323,42 @@ _NODISCARD inline bool operator==(const error_code& _Left, const error_condition return _Left.category().equivalent(_Left.value(), _Right) || _Right.category().equivalent(_Left, _Right.value()); } -_NODISCARD inline bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { - return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value()); -} - _NODISCARD inline bool operator==(const error_condition& _Left, const error_condition& _Right) noexcept { return _Left.category() == _Right.category() && _Left.value() == _Right.value(); } +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts +_NODISCARD inline strong_ordering operator<=>(const error_code& _Left, const error_code& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); +} + +_NODISCARD inline strong_ordering operator<=>(const error_condition& _Left, const error_condition& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); +} + +_NODISCARD inline bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); +} +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + +#if !_HAS_CXX20 +_NODISCARD inline bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { + return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value()); +} + _NODISCARD inline bool operator!=(const error_code& _Left, const error_code& _Right) noexcept { return !(_Left == _Right); } @@ -308,16 +374,7 @@ _NODISCARD inline bool operator!=(const error_condition& _Left, const error_code _NODISCARD inline bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { return !(_Left == _Right); } - -_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); -} - -_NODISCARD inline bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); -} +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS // VIRTUALS FOR error_category diff --git a/stl/inc/thread b/stl/inc/thread index 2d46b78936d..60c41a0fb6d 100644 --- a/stl/inc/thread +++ b/stl/inc/thread @@ -14,6 +14,7 @@ #include #include #if _HAS_CXX20 +#include #include #endif // _HAS_CXX20 @@ -215,7 +216,11 @@ private: friend thread::id thread::get_id() const noexcept; friend thread::id this_thread::get_id() noexcept; friend bool operator==(thread::id _Left, thread::id _Right) noexcept; +#if _HAS_CXX20 + friend strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv friend bool operator<(thread::id _Left, thread::id _Right) noexcept; +#endif // !_HAS_CXX20 template friend basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id); friend hash; @@ -237,6 +242,11 @@ _NODISCARD inline bool operator==(thread::id _Left, thread::id _Right) noexcept return _Left._Id == _Right._Id; } +#if _HAS_CXX20 +_NODISCARD inline strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept { + return _Left._Id <=> _Right._Id; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD inline bool operator!=(thread::id _Left, thread::id _Right) noexcept { return !(_Left == _Right); } @@ -256,6 +266,7 @@ _NODISCARD inline bool operator>(thread::id _Left, thread::id _Right) noexcept { _NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept { return !(_Left < _Right); } +#endif // !_HAS_CXX20 template basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) { diff --git a/stl/inc/tuple b/stl/inc/tuple index d0734801e84..388a8b237e9 100644 --- a/stl/inc/tuple +++ b/stl/inc/tuple @@ -8,6 +8,9 @@ #define _TUPLE_ #include #if _STL_COMPILER_PREPROCESSOR +#ifdef __cpp_lib_concepts +#include +#endif // __cpp_lib_concepts #include #include @@ -233,9 +236,15 @@ public: return true; } - constexpr bool _Less(const tuple&) const noexcept { +#ifdef __cpp_lib_concepts + _NODISCARD constexpr strong_ordering _Three_way_compare(const tuple&) const noexcept { + return strong_ordering::equal; + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + _NODISCARD constexpr bool _Less(const tuple&) const noexcept { return false; } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ }; template @@ -673,11 +682,23 @@ public: return _Myfirst._Val == _Right._Myfirst._Val && _Mybase::_Equals(_Right._Get_rest()); } +#ifdef __cpp_lib_concepts + template , // TRANSITION, DevCom-1344701 + _Synth_three_way_result<_Rest, _Other>...>> // (should be a normal or trailing return type) + _NODISCARD constexpr _Ret _Three_way_compare(const tuple<_First, _Other...>& _Right) const { + if (auto _Result = _Synth_three_way{}(_Myfirst._Val, _Right._Myfirst._Val); _Result != 0) { + return _Result; + } + return _Mybase::_Three_way_compare(_Right._Get_rest()); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template - constexpr bool _Less(const tuple<_Other...>& _Right) const { + _NODISCARD constexpr bool _Less(const tuple<_Other...>& _Right) const { return _Myfirst._Val < _Right._Myfirst._Val || (!(_Right._Myfirst._Val < _Myfirst._Val) && _Mybase::_Less(_Right._Get_rest())); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template friend constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept; @@ -733,10 +754,20 @@ _NODISCARD constexpr bool operator==(const tuple<_Types1...>& _Left, const tuple return _Left._Equals(_Right); } +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Types1, _Types2>...> operator<=>( + const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { + static_assert(sizeof...(_Types1) == sizeof...(_Types2), "cannot compare tuples of different sizes"); + return _Left._Three_way_compare(_Right); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool operator<(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { @@ -758,6 +789,7 @@ template _NODISCARD constexpr bool operator<=(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { return !(_Right < _Left); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template ...>, int> = 0> _CONSTEXPR20 void swap(tuple<_Types...>& _Left, tuple<_Types...>& _Right) noexcept(noexcept(_Left.swap(_Right))) { diff --git a/stl/inc/typeindex b/stl/inc/typeindex index 0aa47537679..68d02e809f7 100644 --- a/stl/inc/typeindex +++ b/stl/inc/typeindex @@ -38,9 +38,17 @@ public: return *_Tptr == *_Right._Tptr; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const type_index& _Right) const noexcept { + return *_Tptr == *_Right._Tptr ? strong_ordering::equal + : _Tptr->before(*_Right._Tptr) ? strong_ordering::less + : strong_ordering::greater; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const type_index& _Right) const noexcept { return !(*this == _Right); } +#endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD bool operator<(const type_index& _Right) const noexcept { return _Tptr->before(*_Right._Tptr); 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 cc191b85356..a909684b495 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -347,10 +347,22 @@ _NODISCARD constexpr bool operator==(const pair<_Ty1, _Ty2>& _Left, const pair<_ return _Left.first == _Right.first && _Left.second == _Right.second; } +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Ty1>, _Synth_three_way_result<_Ty2>> + operator<=>(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { + if (auto _Result = _Synth_three_way{}(_Left.first, _Right.first); _Result != 0) { + return _Result; + } + return _Synth_three_way{}(_Left.second, _Right.second); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool operator<(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { @@ -371,6 +383,7 @@ template _NODISCARD constexpr bool operator>=(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ // ALIAS TEMPLATE _Unrefwrap_t template diff --git a/stl/inc/valarray b/stl/inc/valarray index 63b69f34fa4..5f5a6ab33fc 100644 --- a/stl/inc/valarray +++ b/stl/inc/valarray @@ -1373,6 +1373,12 @@ public: return _Stride; } +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const slice& _Left, const slice& _Right) noexcept /* strengthened */ { + return _Left.start() == _Right.start() && _Left.size() == _Right.size() && _Left.stride() == _Right.stride(); + } +#endif // _HAS_CXX20 + protected: size_t _Start = 0; // the starting offset size_t _Len = 0; // the number of elements diff --git a/stl/inc/variant b/stl/inc/variant index 339410bfd7d..036e1d602aa 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -1383,13 +1383,14 @@ _NODISCARD constexpr add_pointer_t get_if( } // RELATIONAL OPERATORS [variant.relops] -template -struct _Variant_relop_visitor { // evaluate _Op with the contained value of two variants that hold the same alternative +template +struct _Variant_relop_visitor2 { // evaluate _Op with the contained value of two variants that hold the same alternative const _Variant_storage<_Types...>& _Left; template - _NODISCARD constexpr bool operator()(_Tagged _Right) const noexcept( - disjunction_v, is_nothrow_invocable_r>) { + _NODISCARD constexpr _Result operator()(_Tagged _Right) const + noexcept(disjunction_v, + is_nothrow_invocable_r<_Result, _Op, const _Ty&, const _Ty&>>) { // determine the relationship between the stored values of _Left and _Right // pre: _Left.index() == _Idx && _Right.index() == _Idx if constexpr (_Idx != variant_npos) { @@ -1405,7 +1406,7 @@ template _NODISCARD constexpr bool operator==(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if the arguments are both valueless or contain equal values - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Right_index = _Right.index(); return _Left.index() == _Right_index && _Variant_raw_visit(_Right_index, _Right._Storage(), _Visitor{_Left._Storage()}); @@ -1415,7 +1416,7 @@ template _NODISCARD constexpr bool operator!=(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if the arguments have different active alternatives or contain unequal values - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Right_index = _Right.index(); return _Left.index() != _Right_index || _Variant_raw_visit(_Right_index, _Right._Storage(), _Visitor{_Left._Storage()}); @@ -1426,7 +1427,7 @@ _NODISCARD constexpr bool operator<(const variant<_Types...>& _Left, const varia conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left has a lesser index(), or equal index() and lesser // contained value than _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset < _Right_offset @@ -1439,7 +1440,7 @@ _NODISCARD constexpr bool operator>(const variant<_Types...>& _Left, const varia conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left has a greater index(), or equal index() and // greater contained value than _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset > _Right_offset @@ -1452,7 +1453,7 @@ _NODISCARD constexpr bool operator<=(const variant<_Types...>& _Left, const vari conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left's index() is less than _Right's, or equal and // _Left contains a value less than or equal to _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset < _Right_offset @@ -1465,7 +1466,7 @@ _NODISCARD constexpr bool operator>=(const variant<_Types...>& _Left, const vari conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left's index() is greater than _Right's, or equal and // _Left contains a value greater than or equal to _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset > _Right_offset @@ -1473,6 +1474,27 @@ _NODISCARD constexpr bool operator>=(const variant<_Types...>& _Left, const vari && _Variant_raw_visit(_Right_offset - 1, _Right._Storage(), _Visitor{_Left._Storage()})); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires (three_way_comparable<_Types> && ...) +_NODISCARD constexpr common_comparison_category_t...> + operator<=>(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( + conjunction_v...>, + compare_three_way, const _Types&, const _Types&>...>) /* strengthened */ { + // clang-format on + // determine the three-way comparison of _Left's and _Right's index, if equal + // return the three-way comparison of the contained values of _Left and _Right + using _Visitor = _Variant_relop_visitor2...>, _Types...>; + const size_t _Left_offset = _Left.index() + 1; + const size_t _Right_offset = _Right.index() + 1; + const auto _Offset_order = _Left_offset <=> _Right_offset; + return _Offset_order != 0 ? _Offset_order + : _Variant_raw_visit(_Right_offset - 1, _Right._Storage(), _Visitor{_Left._Storage()}); +} +#endif // __cpp_lib_concepts + // VISITATION [variant.visit] template inline constexpr size_t _Variant_total_states = @@ -1716,6 +1738,12 @@ struct monostate {}; _NODISCARD constexpr bool operator==(monostate, monostate) noexcept { return true; } + +#if _HAS_CXX20 +_NODISCARD constexpr strong_ordering operator<=>(monostate, monostate) noexcept { + return strong_ordering::equal; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(monostate, monostate) noexcept { return false; } @@ -1731,6 +1759,7 @@ _NODISCARD constexpr bool operator<=(monostate, monostate) noexcept { _NODISCARD constexpr bool operator>=(monostate, monostate) noexcept { return true; } +#endif // !_HAS_CXX20 // SPECIALIZED ALGORITHMS [variant.specalg] template (const _Vector_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv + _NODISCARD bool operator!=(const _Vector_const_iterator& _Right) const noexcept { return !(*this == _Right); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<(const _Vector_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr < _Right._Ptr; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>(const _Vector_const_iterator& _Right) const noexcept { return _Right < *this; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<=(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<=(const _Vector_const_iterator& _Right) const noexcept { return !(_Right < *this); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>=(const _Vector_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR20_CONTAINER void _Compat(const _Vector_const_iterator& _Right) const noexcept { // test for compatible iterator pair @@ -1829,26 +1836,126 @@ template >, vector(_Iter, _Iter, _Alloc = _Alloc()) -> vector<_Iter_value_t<_Iter>, _Alloc>; #endif // _HAS_CXX17 -template -_CONSTEXPR20_CONTAINER 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 _CONSTEXPR20_CONTAINER 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 constexpr (is_same_v<_Ty, bool>) { + return _STD equal( + _Left._Myvec._Unchecked_begin(), _Left._Myvec._Unchecked_end(), _Right._Myvec._Unchecked_begin()); + } else { + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + } } +#if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20_CONTAINER bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { +_NODISCARD bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 + +// 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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + return 0; +#endif // ^^^ !defined(__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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + return (_Left & _Mask) == 0 ? -1 : 1; +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + } +}; + +#ifdef __cpp_lib_concepts +template +_NODISCARD _CONSTEXPR20_CONTAINER _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 // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD _CONSTEXPR20_CONTAINER 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 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 { + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + } } template @@ -1865,11 +1972,26 @@ template _NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__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 +_CONSTEXPR20_CONTAINER void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { + _Left.swap(_Right); +} + +#if _HAS_CXX20 +template +_CONSTEXPR20_CONTAINER typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { + return _Erase_remove(_Cont, _Val); +} +template +_CONSTEXPR20_CONTAINER 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; @@ -2089,26 +2211,36 @@ public: return this->_Myptr == _Right._Myptr && this->_Myoff == _Right._Myoff; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator!=(const _Vb_const_iterator& _Right) const noexcept { +#if _HAS_CXX20 + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _Vb_const_iterator& _Right) const noexcept { + _Compat(_Right); + if (const auto _CmpResult = this->_Myptr <=> _Right._Myptr; _CmpResult != 0) { + return _CmpResult; + } + return this->_Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv + _NODISCARD bool operator!=(const _Vb_const_iterator& _Right) const noexcept { return !(*this == _Right); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<(const _Vb_const_iterator& _Right) const noexcept { _Compat(_Right); return this->_Myptr < _Right._Myptr || (this->_Myptr == _Right._Myptr && this->_Myoff < _Right._Myoff); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>(const _Vb_const_iterator& _Right) const noexcept { return _Right < *this; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<=(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<=(const _Vb_const_iterator& _Right) const noexcept { return !(_Right < *this); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>=(const _Vb_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR20_CONTAINER void _Compat(const _Vb_const_iterator& _Right) const noexcept { // test for compatible iterator pair @@ -2951,18 +3083,6 @@ public: } }; -template -_NODISCARD _CONSTEXPR20_CONTAINER bool operator==( - const vector& _Left, const vector& _Right) { - return _Left.size() == _Right.size() && _Left._Myvec == _Right._Myvec; -} - -template -_NODISCARD _CONSTEXPR20_CONTAINER bool operator!=( - const vector& _Left, const vector& _Right) { - return !(_Left == _Right); -} - // STRUCT TEMPLATE SPECIALIZATION hash template struct hash> { @@ -2974,18 +3094,6 @@ struct hash> { } }; -#if _HAS_CXX20 -template -_CONSTEXPR20_CONTAINER typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { - return _Erase_remove(_Cont, _Val); -} - -template -_CONSTEXPR20_CONTAINER 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/stl/inc/xcharconv.h b/stl/inc/xcharconv.h index ecd69b6d02e..fff630353ae 100644 --- a/stl/inc/xcharconv.h +++ b/stl/inc/xcharconv.h @@ -39,6 +39,9 @@ _BITMASK_OPS(chars_format) struct to_chars_result { char* ptr; errc ec; +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const to_chars_result&, const to_chars_result&) = default; +#endif // _HAS_CXX20 }; _STD_END diff --git a/stl/inc/xhash b/stl/inc/xhash index 3c8a7bbaba2..a7fc07566a0 100644 --- a/stl/inc/xhash +++ b/stl/inc/xhash @@ -196,12 +196,14 @@ struct _Reinterpret_move_iter { return _Lhs._Base == _Rhs._Base; } +#if !_HAS_CXX20 #ifndef __CUDACC__ // TRANSITION, VSO-568006 _NODISCARD #endif // TRANSITION, VSO-568006 friend bool operator!=(const _Reinterpret_move_iter& _Lhs, const _Reinterpret_move_iter& _Rhs) { return _Lhs._Base != _Rhs._Base; } +#endif // !_HAS_CXX20 }; // STRUCT TEMPLATE _List_head_construct_ptr diff --git a/stl/inc/xlocale b/stl/inc/xlocale index fcdbf63bb17..5fe52ccd945 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -389,9 +389,11 @@ public: return _Ptr == _Loc._Ptr || (name().compare("*") != 0 && name().compare(_Loc.name()) == 0); } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const locale& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 static _MRTIMP2_PURE const locale& __CLRCALL_PURE_OR_CDECL classic(); // classic "C" locale diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 764b7449716..ed52ae8b548 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -887,10 +887,12 @@ _NODISCARD _CONSTEXPR20_DYNALLOC bool operator==(const allocator<_Ty>&, const al return true; } +#if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20_DYNALLOC bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { +_NODISCARD bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { return false; } +#endif // !_HAS_CXX20 #if _HAS_CXX17 // ALIAS TEMPLATE _Guide_size_type_t FOR DEDUCTION GUIDES, N4687 26.5.4.1 [unord.map.overview]/4 diff --git a/stl/inc/xpolymorphic_allocator.h b/stl/inc/xpolymorphic_allocator.h index 47b1bdc68ae..481cc533313 100644 --- a/stl/inc/xpolymorphic_allocator.h +++ b/stl/inc/xpolymorphic_allocator.h @@ -168,9 +168,11 @@ namespace pmr { return &_Left == &_Right || _Left.is_equal(_Right); } +#if !_HAS_CXX20 _NODISCARD inline bool operator!=(const memory_resource& _Left, const memory_resource& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // FUNCTION get_default_resource extern "C" _CRT_SATELLITE_1 memory_resource* __cdecl _Aligned_get_default_resource() noexcept; @@ -299,11 +301,13 @@ namespace pmr { return *_Left.resource() == *_Right.resource(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const polymorphic_allocator<_Ty1>& _Left, const polymorphic_allocator<_Ty2>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 } // namespace pmr diff --git a/stl/inc/xstring b/stl/inc/xstring index 80355987c2c..8551a274080 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -39,6 +39,9 @@ struct _Char_traits { // properties of a string or stream element using pos_type = streampos; using off_type = streamoff; using state_type = _Mbstatet; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 // For copy/move, we can uniformly call memcpy/memmove (or their builtin versions) for all element types. @@ -217,6 +220,9 @@ public: using pos_type = streampos; using off_type = streamoff; using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 using _Primary_char_traits::_Copy_s; using _Primary_char_traits::copy; @@ -355,6 +361,9 @@ public: using pos_type = streampos; using off_type = streamoff; using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 using _Primary_char_traits::_Copy_s; using _Primary_char_traits::copy; @@ -1115,6 +1124,17 @@ public: #endif // _ITERATOR_DEBUG_LEVEL } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _String_view_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, + "cannot compare incompatible string_view iterators"); + return _Myoff <=> _Right._Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr <=> _Right._Myptr; +#endif // _ITERATOR_DEBUG_LEVEL + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const _String_view_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1140,6 +1160,7 @@ public: _NODISCARD constexpr bool operator>=(const _String_view_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL >= 1 friend constexpr void _Verify_range(const _String_view_iterator& _First, const _String_view_iterator& _Last) { @@ -1659,11 +1680,13 @@ _NODISCARD constexpr bool operator==( return _Lhs._Equal(_Rhs); } +#if !_HAS_CXX20 template // TRANSITION, VSO-409326 _NODISCARD constexpr bool operator==( const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { return _Lhs._Equal(_Rhs); } +#endif // !_HAS_CXX20 template // TRANSITION, VSO-409326 _NODISCARD constexpr bool operator==( @@ -1671,7 +1694,7 @@ _NODISCARD constexpr bool operator==( return _Lhs._Equal(_Rhs); } - +#if !_HAS_CXX20 // FUNCTION TEMPLATES operator!= FOR basic_string_view template _NODISCARD constexpr bool operator!=( @@ -1770,7 +1793,37 @@ _NODISCARD constexpr bool operator>=( const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { return _Lhs.compare(_Rhs) >= 0; } +#endif // !_HAS_CXX20 + +#if _HAS_CXX20 +template +struct _Get_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_comparison_category<_Traits, void_t> { + using type = typename _Traits::comparison_category; + + static_assert(_Is_any_of_v, + "N4878 [string.view.comparison]/4: Mandates: R denotes a comparison category type."); +}; + +template +using _Get_comparison_category_t = typename _Get_comparison_category<_Traits>::type; + +template +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} +template // TRANSITION, VSO-409326 +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} +#endif // _HAS_CXX20 // TYPEDEFS FOR basic_string_view using string_view = basic_string_view; @@ -1968,6 +2021,12 @@ public: return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _String_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1988,6 +2047,7 @@ public: _NODISCARD bool operator>=(const _String_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 void _Compat(const _String_const_iterator& _Right) const noexcept { // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL >= 1 @@ -4512,13 +4572,26 @@ _NODISCARD bool operator==( } template -_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { - return _Right._Equal(_Left); +_NODISCARD bool operator==(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { + return _Left._Equal(_Right); } +#if _HAS_CXX20 template -_NODISCARD bool operator==(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { - return _Left._Equal(_Right); +_NODISCARD _Get_comparison_category_t<_Traits> operator<=>( + const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); +} + +template +_NODISCARD _Get_comparison_category_t<_Traits> operator<=>( + const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { + return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template +_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { + return _Right._Equal(_Left); } template @@ -4600,6 +4673,7 @@ template _NODISCARD bool operator>=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { return !(_Left < _Right); } +#endif // ^^^ !_HAS_CXX20 ^^^ using string = basic_string, allocator>; using wstring = basic_string, allocator>; diff --git a/stl/inc/xtree b/stl/inc/xtree index aba7f46baee..3d9e6786ee0 100644 --- a/stl/inc/xtree +++ b/stl/inc/xtree @@ -98,18 +98,22 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Tree_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool operator==(_Default_sentinel) const noexcept { return !!_Ptr->_Isnil; // TRANSITION, avoid warning C4800: // "Implicit conversion from 'char' to bool. Possible information loss" (/Wall) } +#if !_HAS_CXX20 _NODISCARD bool operator!=(_Default_sentinel) const noexcept { return !_Ptr->_Isnil; } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -232,9 +236,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Tree_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _Tree_const_iterator& _First, const _Tree_const_iterator& _Last) noexcept { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 988fb6bcfdd..a5e2e7b509e 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -211,6 +211,7 @@ // P1456R1 Move-Only Views // P1474R1 Helpful Pointers For contiguous_iterator // P1612R1 Relocating endian To +// P1614R2 Adding Spaceship <=> To The Library // P1645R1 constexpr For Algorithms // P1651R0 bind_front() Should Not Unwrap reference_wrapper // P1690R1 Refining Heterogeneous Lookup For Unordered Containers @@ -1252,7 +1253,7 @@ #define __cpp_lib_syncbuf 201803L #ifdef __cpp_lib_concepts // TRANSITION, GH-395 -#define __cpp_lib_three_way_comparison 201711L +#define __cpp_lib_three_way_comparison 201907L #endif // __cpp_lib_concepts #define __cpp_lib_to_address 201711L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index a2496d683f3..c0d3f0eac74 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -320,9 +320,6 @@ std/language.support/support.limits/support.limits.general/functional.version.pa std/language.support/support.limits/support.limits.general/iterator.version.pass.cpp FAIL std/language.support/support.limits/support.limits.general/memory.version.pass.cpp FAIL -# C++20 P1614R2 "Adding Spaceship <=> To The Library" -std/language.support/support.limits/support.limits.general/compare.version.pass.cpp FAIL - # C++23 P1048R1 "is_scoped_enum" std/utilities/meta/meta.unary/meta.unary.prop/is_scoped_enum.pass.cpp FAIL @@ -671,6 +668,9 @@ std/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_lay # Non-Standard assumption that std::filesystem::file_time_type::duration::period is std::nano std/input.output/filesystems/fs.filesystem.synopsis/file_time_type_resolution.compile.pass.cpp FAIL +# P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error +std/iterators/stream.iterators/istream.iterator/istream.iterator.ops/equal.pass.cpp FAIL + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 2760e3e0b3c..0bbc0048e30 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -320,9 +320,6 @@ language.support\support.limits\support.limits.general\functional.version.pass.c language.support\support.limits\support.limits.general\iterator.version.pass.cpp language.support\support.limits\support.limits.general\memory.version.pass.cpp -# C++20 P1614R2 "Adding Spaceship <=> To The Library" -language.support\support.limits\support.limits.general\compare.version.pass.cpp - # C++23 P1048R1 "is_scoped_enum" utilities\meta\meta.unary\meta.unary.prop\is_scoped_enum.pass.cpp @@ -671,6 +668,9 @@ utilities\memory\util.smartptr\util.smartptr.shared\libcxx.control_block_layout. # Non-Standard assumption that std::filesystem::file_time_type::duration::period is std::nano input.output\filesystems\fs.filesystem.synopsis\file_time_type_resolution.compile.pass.cpp +# P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error +iterators\stream.iterators\istream.iterator\istream.iterator.ops\equal.pass.cpp + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/std/test.lst b/tests/std/test.lst index ca60f536b2f..3ac4d56e19c 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 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..43dfd67165b --- /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 2aa72a7adaf..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/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 37fb868db72..2b2e73691e5 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -1,11 +1,9 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// Covers: -// * spaceship for containers - #include #include +#include #include #include #include @@ -14,27 +12,36 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include #include #include #include #include +#include #include #include #include +#include #include #include +#include #include +#include +#include #include #include #include +#include +#include #include -template -using SpaceshipType = decltype(std::declval() <=> std::declval()); - template concept HasSpaceshipWith = requires { std::declval() <=> std::declval(); @@ -142,7 +149,7 @@ struct dummy_diagnostic : std::error_category { }; template -void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { +constexpr bool spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { assert(smaller == smaller_equal); assert(smaller_equal == smaller); assert(smaller != larger); @@ -160,18 +167,22 @@ void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, co assert((smaller <=> smaller_equal) == 0); static_assert(std::is_same_v larger), ReturnType>); + + return true; } template -inline constexpr bool is_pair = false; -template -inline constexpr bool is_pair> = true; // TRANSITION, std::pair spaceship not yet implemented +inline constexpr bool has_synth_ordered = false; +template +inline constexpr bool has_synth_ordered> = true; +template <> +inline constexpr bool has_synth_ordered = true; template void ordered_containers_test(const Container& smaller, const Container& smaller_equal, const Container& larger) { using Elem = typename Container::value_type; - if constexpr (is_pair // TRANSITION, std::pair spaceship not yet implemented - || std::is_same_v) { + + if constexpr (has_synth_ordered) { spaceship_test(smaller, smaller_equal, larger); } else { spaceship_test(smaller, smaller_equal, larger); @@ -185,6 +196,27 @@ void unordered_containers_test( assert(something != different); } +template +void ordered_iterator_test(const Iter& smaller, const Iter& smaller_equal, const Iter& larger, + const ConstIter& const_smaller, const ConstIter& const_smaller_equal, const ConstIter& const_larger) { + spaceship_test(smaller, smaller_equal, larger); + spaceship_test(const_smaller, const_smaller_equal, const_larger); + spaceship_test(const_smaller, smaller_equal, larger); +} + +template +void unordered_iterator_test(const Iter& something, const Iter& something_equal, const Iter& different, + const ConstIter& const_something, const ConstIter& const_something_equal, const ConstIter& const_different) { + assert(something == something_equal); + assert(something != different); + + assert(const_something == const_something_equal); + assert(const_something != const_different); + + assert(something == const_something_equal); + assert(something != const_different); +} + template void diagnostics_test() { dummy_diagnostic c_mem[2]; @@ -215,7 +247,773 @@ void diagnostics_test() { } } +template