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/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/regex b/stl/inc/regex index 84ff2ce169d..2f2949e2683 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -730,35 +730,6 @@ template _NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); } - -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-900973 -// The compiler incorrectly performs overload resolution by preferring implicit conversions over rewrites. -// In C++20 mode the only required comparison operators should be == and <=>. -template -_NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return !(_Left == _Right); -} - -template -_NODISCARD bool operator<(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) < 0; -} - -template -_NODISCARD bool operator>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) > 0; -} - -template -_NODISCARD bool operator<=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) <= 0; -} - -template -_NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) >= 0; -} -#endif // TRANSITION, VSO-900973 #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { @@ -797,70 +768,6 @@ template _NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); } - -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-900973 -// The compiler incorrectly performs overload resolution by preferring implicit conversions over rewrites. -// In C++20 mode the only required comparison operators should be == and <=>. -template -_NODISCARD auto operator<=>(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { - return static_cast::_Comparison_category>(0 <=> (_Right <=> _Left)); -} - -template -_NODISCARD bool operator==(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { - return _Right._Match_equal(_Left); -} - -template -_NODISCARD bool operator!=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { - return !(_Left == _Right); -} - -template -_NODISCARD bool operator<(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) < 0; -} - -template -_NODISCARD bool operator>(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) > 0; -} - -template -_NODISCARD bool operator<=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) <= 0; -} - -template -_NODISCARD bool operator>=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) >= 0; -} - -template -_NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return !(_Left == _Right); -} - -template -_NODISCARD bool operator<(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return (_Left <=> _Right) < 0; -} - -template -_NODISCARD bool operator>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return (_Left <=> _Right) > 0; -} - -template -_NODISCARD bool operator<=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return (_Left <=> _Right) <= 0; -} - -template -_NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return (_Left <=> _Right) >= 0; -} -#endif // TRANSTITION, VSO-900973 #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { @@ -930,70 +837,6 @@ _NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t< return static_cast::_Comparison_category>( _Left._Compare(_STD addressof(_Right), 1) <=> 0); } - -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-900973 -// The compiler incorrectly performs overload resolution by preferring implicit conversions over rewrites. -// In C++20 mode the only required comparison operators should be == and <=>. -template -_NODISCARD auto operator<=>(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return static_cast::_Comparison_category>(0 <=> (_Right <=> _Left)); -} - -template -_NODISCARD bool operator==(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return _Right._Match_equal(_STD addressof(_Left), 1); -} - -template -_NODISCARD bool operator!=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return !(_Left == _Right); -} - -template -_NODISCARD bool operator<(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) < 0; -} - -template -_NODISCARD bool operator>(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) > 0; -} - -template -_NODISCARD bool operator<=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) <= 0; -} - -template -_NODISCARD bool operator>=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) >= 0; -} - -template -_NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return !(_Left == _Right); -} - -template -_NODISCARD bool operator<(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return (_Left <=> _Right) < 0; -} - -template -_NODISCARD bool operator>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return (_Left <=> _Right) > 0; -} - -template -_NODISCARD bool operator<=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return (_Left <=> _Right) <= 0; -} - -template -_NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return (_Left <=> _Right) >= 0; -} -#endif // TRANSITION, VSO-900973 #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { @@ -1064,82 +907,6 @@ _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); } - -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-900973 -// The compiler incorrectly performs overload resolution by preferring implicit conversions over rewrites. -// In C++20 mode the only required comparison operators should be == and <=>. -template -_NODISCARD auto operator<=>( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return static_cast::_Comparison_category>(0 <=> (_Right <=> _Left)); -} - -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 basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) < 0; -} - -template -_NODISCARD bool operator>( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) > 0; -} - -template -_NODISCARD bool operator<=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) <= 0; -} - -template -_NODISCARD bool operator>=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return (_Left <=> _Right) >= 0; -} - -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 sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return (_Left <=> _Right) < 0; -} - -template -_NODISCARD bool operator>( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return (_Left <=> _Right) > 0; -} - -template -_NODISCARD bool operator<=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return (_Left <=> _Right) <= 0; -} - -template -_NODISCARD bool operator>=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return (_Left <=> _Right) >= 0; -} -#endif // TRANSITION, VSO-900973 #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==( diff --git a/stl/inc/tuple b/stl/inc/tuple index d0734801e84..7fb6e3402fc 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/utility b/stl/inc/utility index f14d1cf9bf5..54f6c0f149a 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 @@ -700,37 +713,6 @@ _NODISCARD constexpr bool cmp_greater_equal(const _Ty1 _Left, const _Ty2 _Right) return !_STD cmp_less(_Left, _Right); } -#ifdef __cpp_lib_concepts -// STRUCT _Synth_three_way -struct _Synth_three_way { - // clang-format off - template - _NODISCARD constexpr auto operator()(const _Ty1& _Left, const _Ty2& _Right) const - requires requires { - { _Left < _Right } -> _Boolean_testable; - { _Right < _Left } -> _Boolean_testable; - } - // clang-format on - { - if constexpr (three_way_comparable_with<_Ty1, _Ty2>) { - return _Left <=> _Right; - } else { - if (_Left < _Right) { - return weak_ordering::less; - } else if (_Right < _Left) { - return weak_ordering::greater; - } else { - return weak_ordering::equivalent; - } - } - } -}; - -// ALIAS TEMPLATE _Synth_three_way_result -template -using _Synth_three_way_result = decltype(_Synth_three_way{}(_STD declval<_Ty1&>(), _STD declval<_Ty2&>())); -#endif // __cpp_lib_concepts - // FUNCTION TEMPLATE in_range template _NODISCARD _CONSTEVAL _Ty _Min_limit() noexcept { // same as (numeric_limits<_Ty>::min)(), less throughput cost diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 4d88d87262b..2b2e73691e5 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -40,9 +42,6 @@ #include #include -template -using SpaceshipType = decltype(std::declval() <=> std::declval()); - template concept HasSpaceshipWith = requires { std::declval() <=> std::declval(); @@ -173,15 +172,17 @@ constexpr bool spaceship_test(const SmallType& smaller, const EqualType& smaller } 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); @@ -246,6 +247,83 @@ void diagnostics_test() { } } +template