mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-22 20:01:22 -05:00
The implementation of less<> did not consider the possibility of t < u being
rewritten from overloaded operator<=>. This lead to situation when for t,u that:
* provide overload operator<=>, such that (t < u) is rewritten to (t <=> u) < 0,
* are convertible to pointers,
the expression std::less<>(t, u) would incorrectly result in call of
std::less<void*> on values converted to the pointers, instead of t < u.
The similar issues also occurred for greater<>, less_equal<>, greater_equal<>,
their range equivalents, and in three_way_compare for heterogeneous calls.
This patch addresses above, by also checking for free-functions and member
overloads of operator<=>, before falling back to pointer comparison. We do
not put any constraints on the return type of selected operator, in particular
in being one of the standard defined comparison categories, as the language
does not put any restriction of returned type, and if (t <=> u) is well
formed, (t op u) is interpreted as (t <=> u) op 0. If that later expression
is ill-formed, the expression using op also is (see included tests).
The relational operator rewrites try both order of arguments, t < u,
can be rewritten into operator<=>(t, u) < 0 or 0 < operator<=>(u, t), it
means that we need to test both operator<=>(T, U) and operator<=>(U, T)
if T and U are not the same types. This is now extracted into
__not_overloaded_spaceship helper concept, placed in <concepts>, to
avoid extending set of includes.
The compare_three_way functor defined in compare, already considers overloaded
operator<=>, however it does not consider reversed candidates, leading
to situation in which t <=> u results in 0 <=> operator<=>(u, t), while
compare_three_way{}(t, u) uses pointer comparison. This is also addressed by
using __not_overloaded_spaceship, that check both order of arguments.
Finally, as operator<=> is introduced in C++20, for std::less(_equal)?<>,
std::greater(_equal)?<>, we use provide separate __ptr_cmp implementation
in that mode, that relies on use of requires expression. We use a nested
requires clause to guarantee short-circuiting of their evaluation.
The operator() of aforementioned functors is reworked to use if constexpr,
in all standard modes (as we allow is as extension), eliminating the need
for _S_cmp function.
PR libstdc++/114153
libstdc++-v3/ChangeLog:
* include/bits/ranges_cmp.h (__detail::__less_builtin_ptr_cmp):
Add __not_overloaded_spaceship spaceship check.
* include/bits/stl_function.h (greater<void>::operator())
(less<void>::operator(), greater_equal<void>::operator())
(less_equal<void>::operator()): Implement using if constexpr.
(greater<void>::__S_cmp, less<void>::__S_cmp)
(greater_equal<void>::__ptr_comp, less_equal<void>::S_cmp):
Remove.
(greater<void>::__ptr_cmp, less<void>::__ptr_cmp)
(greater_equal<void>::__ptr_comp, less_equal<void>::ptr_cmp): Change
tostatic constexpr variable. Define in terms of requires expressions
and __not_overloaded_spaceship check.
* include/std/concepts: (__detail::__not_overloaded_spaceship):
Define.
* libsupc++/compare: (__detail::__3way_builtin_ptr_cmp): Use
__not_overloaded_spaceship concept.
* testsuite/20_util/function_objects/comparisons_pointer_spaceship.cc: New test.
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
1293 lines
39 KiB
C++
1293 lines
39 KiB
C++
// -*- C++ -*- operator<=> three-way comparison support.
|
|
|
|
// Copyright (C) 2019-2026 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of GCC.
|
|
//
|
|
// GCC is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
//
|
|
// GCC is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file compare
|
|
* This is a Standard C++ Library header.
|
|
*/
|
|
|
|
#ifndef _COMPARE
|
|
#define _COMPARE
|
|
|
|
#ifdef _GLIBCXX_SYSHDR
|
|
#pragma GCC system_header
|
|
#endif
|
|
|
|
#define __glibcxx_want_concepts
|
|
#define __glibcxx_want_three_way_comparison
|
|
#define __glibcxx_want_type_order
|
|
#include <bits/version.h>
|
|
|
|
#if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
|
|
|
|
#include <concepts>
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic" // __int128
|
|
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
// [cmp.categories], comparison category types
|
|
|
|
namespace __cmp_cat
|
|
{
|
|
using type = signed char;
|
|
|
|
enum class _Ord : type
|
|
{
|
|
equivalent = 0, less = -1, greater = 1,
|
|
// value remains unchanged when negated
|
|
unordered = -__SCHAR_MAX__ - 1
|
|
};
|
|
|
|
template<typename _Ordering>
|
|
[[__gnu__::__always_inline__]]
|
|
constexpr _Ord
|
|
__ord(_Ordering __o) noexcept
|
|
{ return _Ord(__o._M_value); }
|
|
|
|
template<typename _Ordering>
|
|
[[__gnu__::__always_inline__]]
|
|
constexpr _Ordering
|
|
__make(_Ord __o) noexcept
|
|
{ return _Ordering(__o); }
|
|
|
|
struct __literal_zero
|
|
{
|
|
consteval __literal_zero(__literal_zero*) noexcept { }
|
|
};
|
|
}
|
|
|
|
class partial_ordering
|
|
{
|
|
// less=0xff, equiv=0x00, greater=0x01, unordered=0x80
|
|
__cmp_cat::type _M_value;
|
|
|
|
constexpr explicit
|
|
partial_ordering(__cmp_cat::_Ord __v) noexcept
|
|
: _M_value(__cmp_cat::type(__v))
|
|
{ }
|
|
|
|
[[__gnu__::__always_inline__]]
|
|
constexpr __cmp_cat::type
|
|
_M_reverse() const
|
|
{
|
|
// leaves _Ord::unordered unchanged
|
|
return static_cast<__cmp_cat::type>(-_M_value);
|
|
}
|
|
|
|
friend constexpr __cmp_cat::_Ord
|
|
__cmp_cat::__ord<partial_ordering>(partial_ordering) noexcept;
|
|
friend constexpr partial_ordering
|
|
__cmp_cat::__make<partial_ordering>(__cmp_cat::_Ord) noexcept;
|
|
|
|
public:
|
|
// valid values
|
|
static const partial_ordering less;
|
|
static const partial_ordering equivalent;
|
|
static const partial_ordering greater;
|
|
static const partial_ordering unordered;
|
|
|
|
// comparisons
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator==(partial_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value == 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator==(partial_ordering, partial_ordering) noexcept = default;
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator< (partial_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value == -1; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator> (partial_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value == 1; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator<=(partial_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_reverse() >= 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator>=(partial_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value >= 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator< (__cmp_cat::__literal_zero, partial_ordering __v) noexcept
|
|
{ return __v._M_value == 1; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator> (__cmp_cat::__literal_zero, partial_ordering __v) noexcept
|
|
{ return __v._M_value == -1; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator<=(__cmp_cat::__literal_zero, partial_ordering __v) noexcept
|
|
{ return 0 <= __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator>=(__cmp_cat::__literal_zero, partial_ordering __v) noexcept
|
|
{ return 0 <= __v._M_reverse(); }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr partial_ordering
|
|
operator<=>(partial_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr partial_ordering
|
|
operator<=>(__cmp_cat::__literal_zero, partial_ordering __v) noexcept
|
|
{ return partial_ordering(__cmp_cat::_Ord(__v._M_reverse())); }
|
|
};
|
|
|
|
// valid values' definitions
|
|
inline constexpr partial_ordering
|
|
partial_ordering::less(__cmp_cat::_Ord::less);
|
|
|
|
inline constexpr partial_ordering
|
|
partial_ordering::equivalent(__cmp_cat::_Ord::equivalent);
|
|
|
|
inline constexpr partial_ordering
|
|
partial_ordering::greater(__cmp_cat::_Ord::greater);
|
|
|
|
inline constexpr partial_ordering
|
|
partial_ordering::unordered(__cmp_cat::_Ord::unordered);
|
|
|
|
class weak_ordering
|
|
{
|
|
__cmp_cat::type _M_value;
|
|
|
|
constexpr explicit
|
|
weak_ordering(__cmp_cat::_Ord __v) noexcept : _M_value(__cmp_cat::type(__v))
|
|
{ }
|
|
|
|
friend constexpr __cmp_cat::_Ord
|
|
__cmp_cat::__ord<weak_ordering>(weak_ordering) noexcept;
|
|
friend constexpr weak_ordering
|
|
__cmp_cat::__make<weak_ordering>(__cmp_cat::_Ord) noexcept;
|
|
|
|
public:
|
|
// valid values
|
|
static const weak_ordering less;
|
|
static const weak_ordering equivalent;
|
|
static const weak_ordering greater;
|
|
|
|
[[nodiscard]]
|
|
constexpr operator partial_ordering() const noexcept
|
|
{ return __cmp_cat::__make<partial_ordering>(__cmp_cat::_Ord(_M_value)); }
|
|
|
|
// comparisons
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator==(weak_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value == 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator==(weak_ordering, weak_ordering) noexcept = default;
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator< (weak_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value < 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator> (weak_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value > 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator<=(weak_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value <= 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator>=(weak_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value >= 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator< (__cmp_cat::__literal_zero, weak_ordering __v) noexcept
|
|
{ return 0 < __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator> (__cmp_cat::__literal_zero, weak_ordering __v) noexcept
|
|
{ return 0 > __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator<=(__cmp_cat::__literal_zero, weak_ordering __v) noexcept
|
|
{ return 0 <= __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator>=(__cmp_cat::__literal_zero, weak_ordering __v) noexcept
|
|
{ return 0 >= __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr weak_ordering
|
|
operator<=>(weak_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr weak_ordering
|
|
operator<=>(__cmp_cat::__literal_zero, weak_ordering __v) noexcept
|
|
{ return weak_ordering(__cmp_cat::_Ord(-__v._M_value)); }
|
|
};
|
|
|
|
// valid values' definitions
|
|
inline constexpr weak_ordering
|
|
weak_ordering::less(__cmp_cat::_Ord::less);
|
|
|
|
inline constexpr weak_ordering
|
|
weak_ordering::equivalent(__cmp_cat::_Ord::equivalent);
|
|
|
|
inline constexpr weak_ordering
|
|
weak_ordering::greater(__cmp_cat::_Ord::greater);
|
|
|
|
class strong_ordering
|
|
{
|
|
__cmp_cat::type _M_value;
|
|
|
|
constexpr explicit
|
|
strong_ordering(__cmp_cat::_Ord __v) noexcept
|
|
: _M_value(__cmp_cat::type(__v))
|
|
{ }
|
|
|
|
friend constexpr __cmp_cat::_Ord
|
|
__cmp_cat::__ord<strong_ordering>(strong_ordering) noexcept;
|
|
friend constexpr strong_ordering
|
|
__cmp_cat::__make<strong_ordering>(__cmp_cat::_Ord) noexcept;
|
|
|
|
public:
|
|
// valid values
|
|
static const strong_ordering less;
|
|
static const strong_ordering equal;
|
|
static const strong_ordering equivalent;
|
|
static const strong_ordering greater;
|
|
|
|
[[nodiscard]]
|
|
constexpr operator partial_ordering() const noexcept
|
|
{ return __cmp_cat::__make<partial_ordering>(__cmp_cat::_Ord(_M_value)); }
|
|
|
|
[[nodiscard]]
|
|
constexpr operator weak_ordering() const noexcept
|
|
{ return __cmp_cat::__make<weak_ordering>(__cmp_cat::_Ord(_M_value)); }
|
|
|
|
// comparisons
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator==(strong_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value == 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator==(strong_ordering, strong_ordering) noexcept = default;
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator< (strong_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value < 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator> (strong_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value > 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator<=(strong_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value <= 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator>=(strong_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v._M_value >= 0; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator< (__cmp_cat::__literal_zero, strong_ordering __v) noexcept
|
|
{ return 0 < __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator> (__cmp_cat::__literal_zero, strong_ordering __v) noexcept
|
|
{ return 0 > __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator<=(__cmp_cat::__literal_zero, strong_ordering __v) noexcept
|
|
{ return 0 <= __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator>=(__cmp_cat::__literal_zero, strong_ordering __v) noexcept
|
|
{ return 0 >= __v._M_value; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr strong_ordering
|
|
operator<=>(strong_ordering __v, __cmp_cat::__literal_zero) noexcept
|
|
{ return __v; }
|
|
|
|
[[nodiscard]]
|
|
friend constexpr strong_ordering
|
|
operator<=>(__cmp_cat::__literal_zero, strong_ordering __v) noexcept
|
|
{ return strong_ordering(__cmp_cat::_Ord(-__v._M_value)); }
|
|
};
|
|
|
|
// valid values' definitions
|
|
inline constexpr strong_ordering
|
|
strong_ordering::less(__cmp_cat::_Ord::less);
|
|
|
|
inline constexpr strong_ordering
|
|
strong_ordering::equal(__cmp_cat::_Ord::equivalent);
|
|
|
|
inline constexpr strong_ordering
|
|
strong_ordering::equivalent(__cmp_cat::_Ord::equivalent);
|
|
|
|
inline constexpr strong_ordering
|
|
strong_ordering::greater(__cmp_cat::_Ord::greater);
|
|
|
|
|
|
// named comparison functions
|
|
[[nodiscard]]
|
|
constexpr bool
|
|
is_eq(partial_ordering __cmp) noexcept
|
|
{ return __cmp == 0; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool
|
|
is_neq(partial_ordering __cmp) noexcept
|
|
{ return __cmp != 0; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool
|
|
is_lt (partial_ordering __cmp) noexcept
|
|
{ return __cmp < 0; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool
|
|
is_lteq(partial_ordering __cmp) noexcept
|
|
{ return __cmp <= 0; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool
|
|
is_gt (partial_ordering __cmp) noexcept
|
|
{ return __cmp > 0; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool
|
|
is_gteq(partial_ordering __cmp) noexcept
|
|
{ return __cmp >= 0; }
|
|
|
|
namespace __detail
|
|
{
|
|
template<typename _Tp>
|
|
inline constexpr unsigned __cmp_cat_id = 1;
|
|
template<>
|
|
inline constexpr unsigned __cmp_cat_id<partial_ordering> = 2;
|
|
template<>
|
|
inline constexpr unsigned __cmp_cat_id<weak_ordering> = 4;
|
|
template<>
|
|
inline constexpr unsigned __cmp_cat_id<strong_ordering> = 8;
|
|
|
|
template<typename... _Ts>
|
|
constexpr auto __common_cmp_cat()
|
|
{
|
|
constexpr unsigned __cats = (__cmp_cat_id<_Ts> | ...);
|
|
// If any Ti is not a comparison category type, U is void.
|
|
if constexpr (__cats & 1)
|
|
return;
|
|
// Otherwise, if at least one Ti is std::partial_ordering,
|
|
// U is std::partial_ordering.
|
|
else if constexpr (bool(__cats & __cmp_cat_id<partial_ordering>))
|
|
return partial_ordering::equivalent;
|
|
// Otherwise, if at least one Ti is std::weak_ordering,
|
|
// U is std::weak_ordering.
|
|
else if constexpr (bool(__cats & __cmp_cat_id<weak_ordering>))
|
|
return weak_ordering::equivalent;
|
|
// Otherwise, U is std::strong_ordering.
|
|
else
|
|
return strong_ordering::equivalent;
|
|
}
|
|
} // namespace __detail
|
|
|
|
// [cmp.common], common comparison category type
|
|
template<typename... _Ts>
|
|
struct common_comparison_category
|
|
{
|
|
using type = decltype(__detail::__common_cmp_cat<_Ts...>());
|
|
};
|
|
|
|
// Partial specializations for one and zero argument cases.
|
|
|
|
template<typename _Tp>
|
|
struct common_comparison_category<_Tp>
|
|
{ using type = void; };
|
|
|
|
template<>
|
|
struct common_comparison_category<partial_ordering>
|
|
{ using type = partial_ordering; };
|
|
|
|
template<>
|
|
struct common_comparison_category<weak_ordering>
|
|
{ using type = weak_ordering; };
|
|
|
|
template<>
|
|
struct common_comparison_category<strong_ordering>
|
|
{ using type = strong_ordering; };
|
|
|
|
template<>
|
|
struct common_comparison_category<>
|
|
{ using type = strong_ordering; };
|
|
|
|
template<typename... _Ts>
|
|
using common_comparison_category_t
|
|
= typename common_comparison_category<_Ts...>::type;
|
|
|
|
#if __cpp_lib_three_way_comparison >= 201907L
|
|
// C++ >= 20 && impl_3way_comparison >= 201907 && lib_concepts
|
|
namespace __detail
|
|
{
|
|
template<typename _Tp, typename _Cat>
|
|
concept __compares_as
|
|
= same_as<common_comparison_category_t<_Tp, _Cat>, _Cat>;
|
|
} // namespace __detail
|
|
|
|
// [cmp.concept], concept three_way_comparable
|
|
template<typename _Tp, typename _Cat = partial_ordering>
|
|
concept three_way_comparable
|
|
= __detail::__weakly_eq_cmp_with<_Tp, _Tp>
|
|
&& __detail::__partially_ordered_with<_Tp, _Tp>
|
|
&& requires(const remove_reference_t<_Tp>& __a,
|
|
const remove_reference_t<_Tp>& __b)
|
|
{
|
|
{ __a <=> __b } -> __detail::__compares_as<_Cat>;
|
|
};
|
|
|
|
template<typename _Tp, typename _Up, typename _Cat = partial_ordering>
|
|
concept three_way_comparable_with
|
|
= three_way_comparable<_Tp, _Cat> && three_way_comparable<_Up, _Cat>
|
|
&& __detail::__comparison_common_type_with<_Tp, _Up>
|
|
&& three_way_comparable<
|
|
common_reference_t<const remove_reference_t<_Tp>&,
|
|
const remove_reference_t<_Up>&>, _Cat>
|
|
&& __detail::__weakly_eq_cmp_with<_Tp, _Up>
|
|
&& __detail::__partially_ordered_with<_Tp, _Up>
|
|
&& requires(const remove_reference_t<_Tp>& __t,
|
|
const remove_reference_t<_Up>& __u)
|
|
{
|
|
{ __t <=> __u } -> __detail::__compares_as<_Cat>;
|
|
{ __u <=> __t } -> __detail::__compares_as<_Cat>;
|
|
};
|
|
|
|
namespace __detail
|
|
{
|
|
template<typename _Tp, typename _Up>
|
|
using __cmp3way_res_t
|
|
= decltype(std::declval<_Tp>() <=> std::declval<_Up>());
|
|
|
|
// Implementation of std::compare_three_way_result.
|
|
// It is undefined for a program to add specializations of
|
|
// std::compare_three_way_result, so the std::compare_three_way_result_t
|
|
// alias ignores std::compare_three_way_result and uses
|
|
// __detail::__cmp3way_res_impl directly instead.
|
|
template<typename _Tp, typename _Up>
|
|
struct __cmp3way_res_impl
|
|
{ };
|
|
|
|
template<typename _Tp, typename _Up>
|
|
requires requires { typename __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>; }
|
|
struct __cmp3way_res_impl<_Tp, _Up>
|
|
{
|
|
using type = __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>;
|
|
};
|
|
} // namespace __detail
|
|
|
|
/// [cmp.result], result of three-way comparison
|
|
template<typename _Tp, typename _Up = _Tp>
|
|
struct compare_three_way_result
|
|
: __detail::__cmp3way_res_impl<_Tp, _Up>
|
|
{ };
|
|
|
|
/// [cmp.result], result of three-way comparison
|
|
template<typename _Tp, typename _Up = _Tp>
|
|
using compare_three_way_result_t
|
|
= typename __detail::__cmp3way_res_impl<_Tp, _Up>::type;
|
|
|
|
namespace __detail
|
|
{
|
|
// BUILTIN-PTR-THREE-WAY(T, U)
|
|
// This determines whether t <=> u results in a call to a built-in
|
|
// operator<=> comparing pointers. It doesn't work for function pointers
|
|
// (PR 93628).
|
|
template<typename _Tp, typename _Up>
|
|
concept __3way_builtin_ptr_cmp
|
|
= requires(_Tp&& __t, _Up&& __u)
|
|
{ static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u); }
|
|
&& convertible_to<_Tp, const volatile void*>
|
|
&& convertible_to<_Up, const volatile void*>
|
|
&& __not_overloaded_spaceship<_Tp, _Up>;
|
|
} // namespace __detail
|
|
|
|
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
|
// 3530 BUILTIN-PTR-MEOW should not opt the type out of syntactic checks
|
|
|
|
// [cmp.object], typename compare_three_way
|
|
struct compare_three_way
|
|
{
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wc++23-extensions" // static operator()
|
|
template<typename _Tp, typename _Up>
|
|
requires three_way_comparable_with<_Tp, _Up>
|
|
static constexpr auto
|
|
operator() [[nodiscard]] (_Tp&& __t, _Up&& __u)
|
|
noexcept(noexcept(std::declval<_Tp>() <=> std::declval<_Up>()))
|
|
{
|
|
if constexpr (__detail::__3way_builtin_ptr_cmp<_Tp, _Up>)
|
|
{
|
|
auto __pt = static_cast<const volatile void*>(__t);
|
|
auto __pu = static_cast<const volatile void*>(__u);
|
|
if (std::__is_constant_evaluated())
|
|
return __pt <=> __pu;
|
|
auto __it = reinterpret_cast<__UINTPTR_TYPE__>(__pt);
|
|
auto __iu = reinterpret_cast<__UINTPTR_TYPE__>(__pu);
|
|
return __it <=> __iu;
|
|
}
|
|
else
|
|
return static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u);
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
|
|
using is_transparent = void;
|
|
};
|
|
|
|
/// @cond undocumented
|
|
// Namespace for helpers for the <compare> customization points.
|
|
namespace __compare
|
|
{
|
|
template<floating_point _Tp>
|
|
constexpr weak_ordering
|
|
__fp_weak_ordering(_Tp __e, _Tp __f)
|
|
{
|
|
auto __po = __cmp_cat::__ord(__e <=> __f);
|
|
if (__po != __cmp_cat::_Ord::unordered)
|
|
return __cmp_cat::__make<weak_ordering>(__po);
|
|
else // unordered, at least one argument is NaN
|
|
{
|
|
// return -1 for negative nan, +1 for positive nan, 0 otherwise.
|
|
auto __isnan_sign = [](_Tp __fp) -> int {
|
|
return __builtin_isnan(__fp)
|
|
? __builtin_signbit(__fp) ? -1 : 1
|
|
: 0;
|
|
};
|
|
return __isnan_sign(__e) <=> __isnan_sign(__f);
|
|
}
|
|
}
|
|
|
|
void strong_order() = delete;
|
|
|
|
template<typename _Tp, typename _Up>
|
|
concept __adl_strong = requires(_Tp&& __t, _Up&& __u)
|
|
{
|
|
strong_ordering(strong_order(static_cast<_Tp&&>(__t),
|
|
static_cast<_Up&&>(__u)));
|
|
};
|
|
|
|
void weak_order() = delete;
|
|
|
|
template<typename _Tp, typename _Up>
|
|
concept __adl_weak = requires(_Tp&& __t, _Up&& __u)
|
|
{
|
|
weak_ordering(weak_order(static_cast<_Tp&&>(__t),
|
|
static_cast<_Up&&>(__u)));
|
|
};
|
|
|
|
void partial_order() = delete;
|
|
|
|
template<typename _Tp, typename _Up>
|
|
concept __adl_partial = requires(_Tp&& __t, _Up&& __u)
|
|
{
|
|
partial_ordering(partial_order(static_cast<_Tp&&>(__t),
|
|
static_cast<_Up&&>(__u)));
|
|
};
|
|
|
|
template<typename _Ord, typename _Tp, typename _Up>
|
|
concept __cmp3way = requires(_Tp&& __t, _Up&& __u, compare_three_way __c)
|
|
{
|
|
_Ord(__c(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)));
|
|
};
|
|
|
|
template<typename _Tp, typename _Up>
|
|
concept __strongly_ordered
|
|
= __adl_strong<_Tp, _Up>
|
|
|| floating_point<remove_reference_t<_Tp>>
|
|
|| __cmp3way<strong_ordering, _Tp, _Up>;
|
|
|
|
template<typename _Tp, typename _Up>
|
|
concept __decayed_same_as = same_as<decay_t<_Tp>, decay_t<_Up>>;
|
|
|
|
class _Strong_order
|
|
{
|
|
template<typename _Tp, typename _Up>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (floating_point<decay_t<_Tp>>)
|
|
return true;
|
|
else if constexpr (__adl_strong<_Tp, _Up>)
|
|
return noexcept(strong_ordering(strong_order(std::declval<_Tp>(),
|
|
std::declval<_Up>())));
|
|
else if constexpr (__cmp3way<strong_ordering, _Tp, _Up>)
|
|
return noexcept(compare_three_way()(std::declval<_Tp>(),
|
|
std::declval<_Up>()));
|
|
}
|
|
|
|
friend class _Weak_order;
|
|
friend class _Strong_fallback;
|
|
|
|
// Names for the supported floating-point representations.
|
|
enum class _Fp_fmt
|
|
{
|
|
_Binary16, _Binary32, _Binary64, _Binary128, // IEEE
|
|
_X86_80bit, // x86 80-bit extended precision
|
|
_M68k_80bit, // m68k 80-bit extended precision
|
|
_Dbldbl, // IBM 128-bit double-double
|
|
_Bfloat16, // std::bfloat16_t
|
|
};
|
|
|
|
#ifndef __cpp_using_enum
|
|
// XXX Remove these once 'using enum' support is ubiquitous.
|
|
static constexpr _Fp_fmt _Binary16 = _Fp_fmt::_Binary16;
|
|
static constexpr _Fp_fmt _Binary32 = _Fp_fmt::_Binary32;
|
|
static constexpr _Fp_fmt _Binary64 = _Fp_fmt::_Binary64;
|
|
static constexpr _Fp_fmt _Binary128 = _Fp_fmt::_Binary128;
|
|
static constexpr _Fp_fmt _X86_80bit = _Fp_fmt::_X86_80bit;
|
|
static constexpr _Fp_fmt _M68k_80bit = _Fp_fmt::_M68k_80bit;
|
|
static constexpr _Fp_fmt _Dbldbl = _Fp_fmt::_Dbldbl;
|
|
static constexpr _Fp_fmt _Bfloat16 = _Fp_fmt::_Bfloat16;
|
|
#endif
|
|
|
|
// Identify the format used by a floating-point type.
|
|
template<typename _Tp>
|
|
static consteval _Fp_fmt
|
|
_S_fp_fmt() noexcept
|
|
{
|
|
#ifdef __cpp_using_enum
|
|
using enum _Fp_fmt;
|
|
#endif
|
|
|
|
// Identify these formats first, then assume anything else is IEEE.
|
|
// N.B. ARM __fp16 alternative format can be handled as binary16.
|
|
|
|
#ifdef __LONG_DOUBLE_IBM128__
|
|
if constexpr (__is_same(_Tp, long double))
|
|
return _Dbldbl;
|
|
#elif defined __LONG_DOUBLE_IEEE128__ && defined __SIZEOF_IBM128__
|
|
if constexpr (__is_same(_Tp, __ibm128))
|
|
return _Dbldbl;
|
|
#endif
|
|
|
|
#if __LDBL_MANT_DIG__ == 64
|
|
if constexpr (__is_same(_Tp, long double))
|
|
return __LDBL_MIN_EXP__ == -16381 ? _X86_80bit : _M68k_80bit;
|
|
#endif
|
|
#ifdef __SIZEOF_FLOAT80__
|
|
if constexpr (__is_same(_Tp, __float80))
|
|
return _X86_80bit;
|
|
#endif
|
|
#ifdef __STDCPP_BFLOAT16_T__
|
|
if constexpr (__is_same(_Tp, decltype(0.0bf16)))
|
|
return _Bfloat16;
|
|
#endif
|
|
|
|
constexpr int __width = sizeof(_Tp) * __CHAR_BIT__;
|
|
|
|
if constexpr (__width == 16) // IEEE binary16 (or ARM fp16).
|
|
return _Binary16;
|
|
else if constexpr (__width == 32) // IEEE binary32
|
|
return _Binary32;
|
|
else if constexpr (__width == 64) // IEEE binary64
|
|
return _Binary64;
|
|
else if constexpr (__width == 128) // IEEE binary128
|
|
return _Binary128;
|
|
}
|
|
|
|
// So we don't need to include <stdint.h> and pollute the namespace.
|
|
using int64_t = __INT64_TYPE__;
|
|
using int32_t = __INT32_TYPE__;
|
|
using int16_t = __INT16_TYPE__;
|
|
using uint64_t = __UINT64_TYPE__;
|
|
using uint16_t = __UINT16_TYPE__;
|
|
|
|
// Used to unpack floating-point types that do not fit into an integer.
|
|
template<typename _Tp>
|
|
struct _Int
|
|
{
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
uint64_t _M_lo;
|
|
_Tp _M_hi;
|
|
#else
|
|
_Tp _M_hi;
|
|
uint64_t _M_lo;
|
|
#endif
|
|
|
|
constexpr explicit
|
|
_Int(_Tp __hi, uint64_t __lo) noexcept : _M_hi(__hi)
|
|
{ _M_lo = __lo; }
|
|
|
|
constexpr explicit
|
|
_Int(uint64_t __lo) noexcept : _M_hi(0)
|
|
{ _M_lo = __lo; }
|
|
|
|
constexpr bool operator==(const _Int&) const = default;
|
|
|
|
#if defined __hppa__ || (defined __mips__ && !defined __mips_nan2008)
|
|
consteval _Int
|
|
operator<<(int __n) const noexcept
|
|
{
|
|
// XXX this assumes n >= 64, which is true for the use below.
|
|
return _Int(static_cast<_Tp>(_M_lo << (__n - 64)), 0);
|
|
}
|
|
#endif
|
|
|
|
constexpr _Int&
|
|
operator^=(const _Int& __rhs) noexcept
|
|
{
|
|
_M_hi ^= __rhs._M_hi;
|
|
_M_lo ^= __rhs._M_lo;
|
|
return *this;
|
|
}
|
|
|
|
constexpr strong_ordering
|
|
operator<=>(const _Int& __rhs) const noexcept
|
|
{
|
|
strong_ordering __cmp = _M_hi <=> __rhs._M_hi;
|
|
if (__cmp != strong_ordering::equal)
|
|
return __cmp;
|
|
return _M_lo <=> __rhs._M_lo;
|
|
}
|
|
};
|
|
|
|
template<typename _Tp>
|
|
static constexpr _Tp
|
|
_S_compl(_Tp __t) noexcept
|
|
{
|
|
constexpr int __width = sizeof(_Tp) * __CHAR_BIT__;
|
|
// Sign extend to get all ones or all zeros.
|
|
make_unsigned_t<_Tp> __sign = __t >> (__width - 1);
|
|
// If the sign bit was set, this flips all bits below it.
|
|
// This converts ones' complement to two's complement.
|
|
return __t ^ (__sign >> 1);
|
|
}
|
|
|
|
// As above but works on both parts of _Int<T>.
|
|
template<typename _Tp>
|
|
static constexpr _Int<_Tp>
|
|
_S_compl(_Int<_Tp> __t) noexcept
|
|
{
|
|
constexpr int __width = sizeof(_Tp) * __CHAR_BIT__;
|
|
make_unsigned_t<_Tp> __sign = __t._M_hi >> (__width - 1);
|
|
__t._M_hi ^= (__sign >> 1 );
|
|
uint64_t __sign64 = (_Tp)__sign;
|
|
__t._M_lo ^= __sign64;
|
|
return __t;
|
|
}
|
|
|
|
// Bit-cast a floating-point value to an unsigned integer.
|
|
template<typename _Tp>
|
|
constexpr static auto
|
|
_S_fp_bits(_Tp __val) noexcept
|
|
{
|
|
if constexpr (sizeof(_Tp) == sizeof(int64_t))
|
|
return __builtin_bit_cast(int64_t, __val);
|
|
else if constexpr (sizeof(_Tp) == sizeof(int32_t))
|
|
return __builtin_bit_cast(int32_t, __val);
|
|
else if constexpr (sizeof(_Tp) == sizeof(int16_t))
|
|
return __builtin_bit_cast(int16_t, __val);
|
|
else
|
|
{
|
|
#ifdef __cpp_using_enum
|
|
using enum _Fp_fmt;
|
|
#endif
|
|
constexpr auto __fmt = _S_fp_fmt<_Tp>();
|
|
if constexpr (__fmt == _X86_80bit)
|
|
{
|
|
if constexpr (sizeof(_Tp) == 3 * sizeof(int32_t))
|
|
{
|
|
auto __ival = __builtin_bit_cast(_Int<int32_t>, __val);
|
|
return _Int<int16_t>(__ival._M_hi, __ival._M_lo);
|
|
}
|
|
else
|
|
{
|
|
auto __ival = __builtin_bit_cast(_Int<int64_t>, __val);
|
|
return _Int<int16_t>(__ival._M_hi, __ival._M_lo);
|
|
}
|
|
}
|
|
else if constexpr (__fmt == _M68k_80bit)
|
|
{
|
|
auto __ival = __builtin_bit_cast(_Int<int32_t>, __val);
|
|
return _Int<int16_t>(__ival._M_hi >> 16, __ival._M_lo);
|
|
}
|
|
else if constexpr (sizeof(_Tp) == 2 * sizeof(int64_t))
|
|
{
|
|
#if __SIZEOF_INT128__
|
|
return __builtin_bit_cast(__int128, __val);
|
|
#else
|
|
return __builtin_bit_cast(_Int<int64_t>, __val);
|
|
#endif
|
|
}
|
|
else
|
|
static_assert(sizeof(_Tp) == sizeof(int64_t),
|
|
"unsupported floating-point type");
|
|
}
|
|
}
|
|
|
|
template<typename _Tp>
|
|
static constexpr strong_ordering
|
|
_S_fp_cmp(_Tp __x, _Tp __y) noexcept
|
|
{
|
|
#ifdef __vax__
|
|
if (__builtin_isnan(__x) || __builtin_isnan(__y))
|
|
{
|
|
int __ix = (bool) __builtin_isnan(__x);
|
|
int __iy = (bool) __builtin_isnan(__y);
|
|
__ix *= __builtin_signbit(__x) ? -1 : 1;
|
|
__iy *= __builtin_signbit(__y) ? -1 : 1;
|
|
return __ix <=> __iy;
|
|
}
|
|
else
|
|
return __builtin_bit_cast(strong_ordering, __x <=> __y);
|
|
#endif
|
|
|
|
auto __ix = _S_fp_bits(__x);
|
|
auto __iy = _S_fp_bits(__y);
|
|
|
|
if (__ix == __iy)
|
|
return strong_ordering::equal; // All bits are equal, we're done.
|
|
|
|
#ifdef __cpp_using_enum
|
|
using enum _Fp_fmt;
|
|
#endif
|
|
constexpr auto __fmt = _S_fp_fmt<_Tp>();
|
|
|
|
if constexpr (__fmt == _Dbldbl) // double-double
|
|
{
|
|
// Unpack the double-double into two parts.
|
|
// We never inspect the low double as a double, cast to integer.
|
|
struct _Unpacked { double _M_hi; int64_t _M_lo; };
|
|
auto __x2 = __builtin_bit_cast(_Unpacked, __x);
|
|
auto __y2 = __builtin_bit_cast(_Unpacked, __y);
|
|
|
|
// Compare the high doubles first and use result if unequal.
|
|
auto __cmp = _S_fp_cmp(__x2._M_hi, __y2._M_hi);
|
|
if (__cmp != strong_ordering::equal)
|
|
return __cmp;
|
|
|
|
// For NaN the low double is unused, so if the high doubles
|
|
// are the same NaN, we don't need to compare the low doubles.
|
|
if (__builtin_isnan(__x2._M_hi))
|
|
return strong_ordering::equal;
|
|
// Similarly, if the low doubles are +zero or -zero (which is
|
|
// true for all infinities and some other values), we're done.
|
|
if (((__x2._M_lo | __y2._M_lo) & 0x7fffffffffffffffULL) == 0)
|
|
return strong_ordering::equal;
|
|
|
|
// Otherwise, compare the low parts.
|
|
return _S_compl(__x2._M_lo) <=> _S_compl(__y2._M_lo);
|
|
}
|
|
else
|
|
{
|
|
if constexpr (__fmt == _M68k_80bit)
|
|
{
|
|
// For m68k the MSB of the significand is ignored for the
|
|
// greatest exponent, so either 0 or 1 is valid there.
|
|
// Set it before comparing, so that we never have 0 there.
|
|
constexpr uint16_t __maxexp = 0x7fff;
|
|
if ((__ix._M_hi & __maxexp) == __maxexp)
|
|
__ix._M_lo |= 1ull << 63;
|
|
if ((__iy._M_hi & __maxexp) == __maxexp)
|
|
__iy._M_lo |= 1ull << 63;
|
|
}
|
|
else
|
|
{
|
|
#if defined __hppa__ || (defined __mips__ && !defined __mips_nan2008)
|
|
// IEEE 754-1985 allowed the meaning of the quiet/signaling
|
|
// bit to be reversed. Flip that to give desired ordering.
|
|
if (__builtin_isnan(__x) && __builtin_isnan(__y))
|
|
{
|
|
using _Int = decltype(__ix);
|
|
|
|
constexpr int __nantype = __fmt == _Binary32 ? 22
|
|
: __fmt == _Binary64 ? 51
|
|
: __fmt == _Binary128 ? 111
|
|
: -1;
|
|
constexpr _Int __bit = _Int(1) << __nantype;
|
|
__ix ^= __bit;
|
|
__iy ^= __bit;
|
|
}
|
|
#endif
|
|
}
|
|
return _S_compl(__ix) <=> _S_compl(__iy);
|
|
}
|
|
}
|
|
|
|
public:
|
|
template<typename _Tp, __decayed_same_as<_Tp> _Up>
|
|
requires __strongly_ordered<_Tp, _Up>
|
|
constexpr strong_ordering
|
|
operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const
|
|
noexcept(_S_noexcept<_Tp, _Up>())
|
|
{
|
|
if constexpr (floating_point<decay_t<_Tp>>)
|
|
return _S_fp_cmp(__e, __f);
|
|
else if constexpr (__adl_strong<_Tp, _Up>)
|
|
return strong_ordering(strong_order(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f)));
|
|
else if constexpr (__cmp3way<strong_ordering, _Tp, _Up>)
|
|
return compare_three_way()(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f));
|
|
}
|
|
};
|
|
|
|
template<typename _Tp, typename _Up>
|
|
concept __weakly_ordered
|
|
= floating_point<remove_reference_t<_Tp>>
|
|
|| __adl_weak<_Tp, _Up>
|
|
|| __cmp3way<weak_ordering, _Tp, _Up>
|
|
|| __strongly_ordered<_Tp, _Up>;
|
|
|
|
class _Weak_order
|
|
{
|
|
template<typename _Tp, typename _Up>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (floating_point<decay_t<_Tp>>)
|
|
return true;
|
|
else if constexpr (__adl_weak<_Tp, _Up>)
|
|
return noexcept(weak_ordering(weak_order(std::declval<_Tp>(),
|
|
std::declval<_Up>())));
|
|
else if constexpr (__cmp3way<weak_ordering, _Tp, _Up>)
|
|
return noexcept(compare_three_way()(std::declval<_Tp>(),
|
|
std::declval<_Up>()));
|
|
else if constexpr (__strongly_ordered<_Tp, _Up>)
|
|
return _Strong_order::_S_noexcept<_Tp, _Up>();
|
|
}
|
|
|
|
friend class _Partial_order;
|
|
friend class _Weak_fallback;
|
|
|
|
public:
|
|
template<typename _Tp, __decayed_same_as<_Tp> _Up>
|
|
requires __weakly_ordered<_Tp, _Up>
|
|
constexpr weak_ordering
|
|
operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const
|
|
noexcept(_S_noexcept<_Tp, _Up>())
|
|
{
|
|
if constexpr (floating_point<decay_t<_Tp>>)
|
|
return __compare::__fp_weak_ordering(__e, __f);
|
|
else if constexpr (__adl_weak<_Tp, _Up>)
|
|
return weak_ordering(weak_order(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f)));
|
|
else if constexpr (__cmp3way<weak_ordering, _Tp, _Up>)
|
|
return compare_three_way()(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f));
|
|
else if constexpr (__strongly_ordered<_Tp, _Up>)
|
|
return _Strong_order{}(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f));
|
|
}
|
|
};
|
|
|
|
template<typename _Tp, typename _Up>
|
|
concept __partially_ordered
|
|
= __adl_partial<_Tp, _Up>
|
|
|| __cmp3way<partial_ordering, _Tp, _Up>
|
|
|| __weakly_ordered<_Tp, _Up>;
|
|
|
|
class _Partial_order
|
|
{
|
|
template<typename _Tp, typename _Up>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (__adl_partial<_Tp, _Up>)
|
|
return noexcept(partial_ordering(partial_order(std::declval<_Tp>(),
|
|
std::declval<_Up>())));
|
|
else if constexpr (__cmp3way<partial_ordering, _Tp, _Up>)
|
|
return noexcept(compare_three_way()(std::declval<_Tp>(),
|
|
std::declval<_Up>()));
|
|
else if constexpr (__weakly_ordered<_Tp, _Up>)
|
|
return _Weak_order::_S_noexcept<_Tp, _Up>();
|
|
}
|
|
|
|
friend class _Partial_fallback;
|
|
|
|
public:
|
|
template<typename _Tp, __decayed_same_as<_Tp> _Up>
|
|
requires __partially_ordered<_Tp, _Up>
|
|
constexpr partial_ordering
|
|
operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const
|
|
noexcept(_S_noexcept<_Tp, _Up>())
|
|
{
|
|
if constexpr (__adl_partial<_Tp, _Up>)
|
|
return partial_ordering(partial_order(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f)));
|
|
else if constexpr (__cmp3way<partial_ordering, _Tp, _Up>)
|
|
return compare_three_way()(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f));
|
|
else if constexpr (__weakly_ordered<_Tp, _Up>)
|
|
return _Weak_order{}(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f));
|
|
}
|
|
};
|
|
|
|
template<typename _Tp, typename _Up>
|
|
concept __op_eq_lt = requires(_Tp&& __t, _Up&& __u)
|
|
{
|
|
{ static_cast<_Tp&&>(__t) == static_cast<_Up&&>(__u) }
|
|
-> convertible_to<bool>;
|
|
{ static_cast<_Tp&&>(__t) < static_cast<_Up&&>(__u) }
|
|
-> convertible_to<bool>;
|
|
};
|
|
|
|
class _Strong_fallback
|
|
{
|
|
template<typename _Tp, typename _Up>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (__strongly_ordered<_Tp, _Up>)
|
|
return _Strong_order::_S_noexcept<_Tp, _Up>();
|
|
else
|
|
return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
|
|
&& noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
|
|
}
|
|
|
|
public:
|
|
template<typename _Tp, __decayed_same_as<_Tp> _Up>
|
|
requires __strongly_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up>
|
|
constexpr strong_ordering
|
|
operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const
|
|
noexcept(_S_noexcept<_Tp, _Up>())
|
|
{
|
|
if constexpr (__strongly_ordered<_Tp, _Up>)
|
|
return _Strong_order{}(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f));
|
|
else // __op_eq_lt<_Tp, _Up>
|
|
return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
|
|
? strong_ordering::equal
|
|
: static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
|
|
? strong_ordering::less
|
|
: strong_ordering::greater;
|
|
}
|
|
};
|
|
|
|
class _Weak_fallback
|
|
{
|
|
template<typename _Tp, typename _Up>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (__weakly_ordered<_Tp, _Up>)
|
|
return _Weak_order::_S_noexcept<_Tp, _Up>();
|
|
else
|
|
return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
|
|
&& noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
|
|
}
|
|
|
|
public:
|
|
template<typename _Tp, __decayed_same_as<_Tp> _Up>
|
|
requires __weakly_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up>
|
|
constexpr weak_ordering
|
|
operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const
|
|
noexcept(_S_noexcept<_Tp, _Up>())
|
|
{
|
|
if constexpr (__weakly_ordered<_Tp, _Up>)
|
|
return _Weak_order{}(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f));
|
|
else // __op_eq_lt<_Tp, _Up>
|
|
return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
|
|
? weak_ordering::equivalent
|
|
: static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
|
|
? weak_ordering::less
|
|
: weak_ordering::greater;
|
|
}
|
|
};
|
|
|
|
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
|
// 3465. compare_partial_order_fallback requires F < E
|
|
template<typename _Tp, typename _Up>
|
|
concept __op_eq_lt_lt = __op_eq_lt<_Tp, _Up>
|
|
&& requires(_Tp&& __t, _Up&& __u)
|
|
{
|
|
{ static_cast<_Up&&>(__u) < static_cast<_Tp&&>(__t) }
|
|
-> convertible_to<bool>;
|
|
};
|
|
|
|
class _Partial_fallback
|
|
{
|
|
template<typename _Tp, typename _Up>
|
|
static constexpr bool
|
|
_S_noexcept()
|
|
{
|
|
if constexpr (__partially_ordered<_Tp, _Up>)
|
|
return _Partial_order::_S_noexcept<_Tp, _Up>();
|
|
else
|
|
return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
|
|
&& noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
|
|
}
|
|
|
|
public:
|
|
template<typename _Tp, __decayed_same_as<_Tp> _Up>
|
|
requires __partially_ordered<_Tp, _Up> || __op_eq_lt_lt<_Tp, _Up>
|
|
constexpr partial_ordering
|
|
operator() [[nodiscard]] (_Tp&& __e, _Up&& __f) const
|
|
noexcept(_S_noexcept<_Tp, _Up>())
|
|
{
|
|
if constexpr (__partially_ordered<_Tp, _Up>)
|
|
return _Partial_order{}(static_cast<_Tp&&>(__e),
|
|
static_cast<_Up&&>(__f));
|
|
else // __op_eq_lt_lt<_Tp, _Up>
|
|
return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
|
|
? partial_ordering::equivalent
|
|
: static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
|
|
? partial_ordering::less
|
|
: static_cast<_Up&&>(__f) < static_cast<_Tp&&>(__e)
|
|
? partial_ordering::greater
|
|
: partial_ordering::unordered;
|
|
}
|
|
};
|
|
} // namespace @endcond
|
|
|
|
// [cmp.alg], comparison algorithms
|
|
|
|
inline namespace _Cpo
|
|
{
|
|
inline constexpr __compare::_Strong_order strong_order{};
|
|
|
|
inline constexpr __compare::_Weak_order weak_order{};
|
|
|
|
inline constexpr __compare::_Partial_order partial_order{};
|
|
|
|
inline constexpr __compare::_Strong_fallback
|
|
compare_strong_order_fallback{};
|
|
|
|
inline constexpr __compare::_Weak_fallback
|
|
compare_weak_order_fallback{};
|
|
|
|
inline constexpr __compare::_Partial_fallback
|
|
compare_partial_order_fallback{};
|
|
}
|
|
|
|
/// @cond undocumented
|
|
namespace __detail
|
|
{
|
|
// [expos.only.func] synth-three-way
|
|
inline constexpr struct _Synth3way
|
|
{
|
|
template<typename _Tp, typename _Up>
|
|
static constexpr bool
|
|
_S_noexcept(const _Tp* __t = nullptr, const _Up* __u = nullptr)
|
|
{
|
|
if constexpr (three_way_comparable_with<_Tp, _Up>)
|
|
return noexcept(*__t <=> *__u);
|
|
else
|
|
return noexcept(*__t < *__u) && noexcept(*__u < *__t);
|
|
}
|
|
|
|
template<typename _Tp, typename _Up>
|
|
[[nodiscard]]
|
|
constexpr auto
|
|
operator()(const _Tp& __t, const _Up& __u) const
|
|
noexcept(_S_noexcept<_Tp, _Up>())
|
|
requires requires
|
|
{
|
|
{ __t < __u } -> __boolean_testable;
|
|
{ __u < __t } -> __boolean_testable;
|
|
}
|
|
{
|
|
if constexpr (three_way_comparable_with<_Tp, _Up>)
|
|
return __t <=> __u;
|
|
else
|
|
{
|
|
if (__t < __u)
|
|
return weak_ordering::less;
|
|
else if (__u < __t)
|
|
return weak_ordering::greater;
|
|
else
|
|
return weak_ordering::equivalent;
|
|
}
|
|
}
|
|
} __synth3way = {};
|
|
|
|
// [expos.only.func] synth-three-way-result
|
|
template<typename _Tp, typename _Up = _Tp>
|
|
using __synth3way_t
|
|
= decltype(__detail::__synth3way(std::declval<_Tp&>(),
|
|
std::declval<_Up&>()));
|
|
} // namespace __detail
|
|
/// @endcond
|
|
|
|
#if __glibcxx_type_order >= 202506L // C++ >= 26
|
|
/// Total ordering of types.
|
|
/// @since C++26
|
|
|
|
template<typename _Tp, typename _Up>
|
|
struct type_order
|
|
{
|
|
static constexpr strong_ordering value = __builtin_type_order(_Tp, _Up);
|
|
using value_type = strong_ordering;
|
|
constexpr operator value_type() const noexcept { return value; }
|
|
constexpr value_type operator()() const noexcept { return value; }
|
|
};
|
|
|
|
/// @ingroup variable_templates
|
|
/// @since C++26
|
|
template<typename _Tp, typename _Up>
|
|
inline constexpr strong_ordering type_order_v
|
|
= __builtin_type_order(_Tp, _Up);
|
|
#endif // __glibcxx_type_order >= 202506L
|
|
|
|
#endif // __cpp_lib_three_way_comparison >= 201907L
|
|
} // namespace std
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
#endif // C++20
|
|
|
|
#endif // _COMPARE
|