зеркало из https://github.com/microsoft/STL.git
<compare>,<functional> Concept-constrained comparisons (#385)
Includes: * concepts `three_way_comparable` and `three_way_comparable_with`, * type trait `std::compare_three_way_result` (with `_t` alias), and * function object `compare_three_way`. in `<compare>`, and: * function objects `ranges::equal_to` and `ranges::less` (in `<xutility>` for easy algorithm access), `ranges::not_equal_to`, `ranges::less_equal`, `ranges::greater`, and `ranges::greater_equal` (in `<functional>`), * slight refactoring of concept definitions in `<concepts>` to avoid redundant requirements for the single-type comparison concepts `equality_comparable`, `totally_ordered`, and `three_way_comparable`, * heavy refactoring of the trait `common_comparison_category` (and `_t` alias) in `<compare>` to requires only `n + c` template instantiations instead of `cn` template instantiations, * ======== ABI BREAK =========== remove the `_Is_unordered` member from `std::partial_ordering` in `<compare>` since (a) it's true if and only if the stored value has a particular value, and (b) Clang expects all of the comparison category types to have size 1, * reorder all `is_transparent` alias declarations in the STL to be after the corresponding `operator()` to agree with synopsis order. Also adds a new test `P0896R4_P1614R2_comparisons` that exercises the above. The ABI BREAK sounds scary - as it should - but note that: * this is a `/std:c++latest` feature, which is subject to change, * objects of comparison category type aren't typically stored, * only MSVC has released `<compare>` so far, so it's not widely used. Altogether, it's extremely unlikely that anyone has encoded this in ABI.
This commit is contained in:
Родитель
2b2746dd78
Коммит
f01ecbbe8e
|
@ -45,7 +45,9 @@ struct _Optimistic_temporary_buffer { // temporary storage with _alloca-like att
|
|||
template <class _Diff>
|
||||
explicit _Optimistic_temporary_buffer(const _Diff _Requested_size) noexcept { // get temporary storage
|
||||
const auto _Attempt = _Temporary_buffer_size(_Requested_size);
|
||||
if (_Requested_size <= _Optimistic_count) { // unconditionally engage stack space
|
||||
// Since _Diff is a count of elements in a forward range, and forward iterators must denote objects in memory,
|
||||
// it must fit in a size_t.
|
||||
if (static_cast<size_t>(_Requested_size) <= _Optimistic_count) { // unconditionally engage stack space
|
||||
_Data = reinterpret_cast<_Ty*>(&_Stack_space[0]);
|
||||
_Capacity = static_cast<ptrdiff_t>(_Requested_size); // in bounds due to if condition
|
||||
return;
|
||||
|
|
127
stl/inc/compare
127
stl/inc/compare
|
@ -12,7 +12,11 @@
|
|||
#if !_HAS_CXX20
|
||||
#pragma message("The contents of <compare> are available only with C++20 or later.")
|
||||
#else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv
|
||||
#ifdef __cpp_lib_concepts
|
||||
#include <concepts>
|
||||
#else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv
|
||||
#include <xtr1common>
|
||||
#endif // !__cpp_lib_concepts
|
||||
|
||||
#pragma pack(push, _CRT_PACKING)
|
||||
#pragma warning(push, _STL_WARNING_LEVEL)
|
||||
|
@ -34,11 +38,11 @@ enum class _Compare_ncmp : _Compare_t { unordered = -127 };
|
|||
class partial_ordering {
|
||||
public:
|
||||
_NODISCARD constexpr explicit partial_ordering(const _Compare_eq _Value_) noexcept
|
||||
: _Value(static_cast<_Compare_t>(_Value_)), _Is_ordered(true) {}
|
||||
: _Value(static_cast<_Compare_t>(_Value_)) {}
|
||||
_NODISCARD constexpr explicit partial_ordering(const _Compare_ord _Value_) noexcept
|
||||
: _Value(static_cast<_Compare_t>(_Value_)), _Is_ordered(true) {}
|
||||
: _Value(static_cast<_Compare_t>(_Value_)) {}
|
||||
_NODISCARD constexpr explicit partial_ordering(const _Compare_ncmp _Value_) noexcept
|
||||
: _Value(static_cast<_Compare_t>(_Value_)), _Is_ordered(false) {}
|
||||
: _Value(static_cast<_Compare_t>(_Value_)) {}
|
||||
|
||||
static const partial_ordering less;
|
||||
static const partial_ordering equivalent;
|
||||
|
@ -46,7 +50,7 @@ public:
|
|||
static const partial_ordering unordered;
|
||||
|
||||
_NODISCARD friend constexpr bool operator==(const partial_ordering _Val, _Literal_zero) noexcept {
|
||||
return _Val._Is_ordered && _Val._Value == 0;
|
||||
return _Val._Value == 0;
|
||||
}
|
||||
|
||||
#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L
|
||||
|
@ -56,48 +60,48 @@ public:
|
|||
friend constexpr bool operator==(const partial_ordering&, const partial_ordering&) noexcept = default;
|
||||
#else // ^^^ supports <=> and P1185 / supports neither vvv
|
||||
_NODISCARD friend constexpr bool operator!=(const partial_ordering _Val, _Literal_zero) noexcept {
|
||||
return !_Val._Is_ordered || _Val._Value != 0;
|
||||
return _Val._Value != 0;
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator==(_Literal_zero, const partial_ordering _Val) noexcept {
|
||||
return _Val._Is_ordered && 0 == _Val._Value;
|
||||
return 0 == _Val._Value;
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator!=(_Literal_zero, const partial_ordering _Val) noexcept {
|
||||
return !_Val._Is_ordered || 0 != _Val._Value;
|
||||
return 0 != _Val._Value;
|
||||
}
|
||||
#endif // defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L
|
||||
|
||||
_NODISCARD friend constexpr bool operator<(const partial_ordering _Val, _Literal_zero) noexcept {
|
||||
return _Val._Is_ordered && _Val._Value < 0;
|
||||
return _Val._Value == static_cast<_Compare_t>(_Compare_ord::less);
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator>(const partial_ordering _Val, _Literal_zero) noexcept {
|
||||
return _Val._Is_ordered && _Val._Value > 0;
|
||||
return _Val._Value > 0;
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator<=(const partial_ordering _Val, _Literal_zero) noexcept {
|
||||
return _Val._Is_ordered && _Val._Value <= 0;
|
||||
return _Val._Value <= 0 && _Val._Is_ordered();
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator>=(const partial_ordering _Val, _Literal_zero) noexcept {
|
||||
return _Val._Is_ordered && _Val._Value >= 0;
|
||||
return _Val._Value >= 0;
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator<(_Literal_zero, const partial_ordering _Val) noexcept {
|
||||
return _Val._Is_ordered && 0 < _Val._Value;
|
||||
return 0 < _Val._Value;
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator>(_Literal_zero, const partial_ordering _Val) noexcept {
|
||||
return _Val._Is_ordered && 0 > _Val._Value;
|
||||
return 0 > _Val._Value && _Val._Is_ordered();
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator<=(_Literal_zero, const partial_ordering _Val) noexcept {
|
||||
return _Val._Is_ordered && 0 <= _Val._Value;
|
||||
return 0 <= _Val._Value;
|
||||
}
|
||||
|
||||
_NODISCARD friend constexpr bool operator>=(_Literal_zero, const partial_ordering _Val) noexcept {
|
||||
return _Val._Is_ordered && 0 >= _Val._Value;
|
||||
return 0 >= _Val._Value && _Val._Is_ordered();
|
||||
}
|
||||
|
||||
#ifdef __cpp_impl_three_way_comparison
|
||||
|
@ -111,8 +115,11 @@ public:
|
|||
#endif // __cpp_impl_three_way_comparison
|
||||
|
||||
private:
|
||||
_NODISCARD constexpr bool _Is_ordered() const noexcept {
|
||||
return _Value != static_cast<_Compare_t>(_Compare_ncmp::unordered);
|
||||
}
|
||||
|
||||
_Compare_t _Value;
|
||||
bool _Is_ordered;
|
||||
};
|
||||
|
||||
inline constexpr partial_ordering partial_ordering::less(_Compare_ord::less);
|
||||
|
@ -335,23 +342,83 @@ _NODISCARD constexpr bool is_gteq(const partial_ordering _Comp) noexcept {
|
|||
}
|
||||
|
||||
// ALIAS TEMPLATE common_comparison_category_t
|
||||
template <class _Ty>
|
||||
inline constexpr bool _Is_comparison_category = _Is_any_of_v<_Ty, partial_ordering, weak_ordering, strong_ordering>;
|
||||
|
||||
template <class... _Types>
|
||||
struct common_comparison_category {
|
||||
static constexpr bool _All_comparison_categories = (_Is_comparison_category<_Types> && ...);
|
||||
static constexpr bool _At_least_one_partial_ordering = _Is_any_of_v<partial_ordering, _Types...>;
|
||||
static constexpr bool _At_least_one_weak_ordering = _Is_any_of_v<weak_ordering, _Types...>;
|
||||
|
||||
using type = conditional_t<_All_comparison_categories,
|
||||
conditional_t<_At_least_one_partial_ordering, partial_ordering,
|
||||
conditional_t<_At_least_one_weak_ordering, weak_ordering, strong_ordering>>,
|
||||
void>;
|
||||
enum _Comparison_category : unsigned char {
|
||||
_Comparison_category_none = 1,
|
||||
_Comparison_category_partial = 2,
|
||||
_Comparison_category_weak = 4,
|
||||
_Comparison_category_strong = 0,
|
||||
};
|
||||
|
||||
template <class... _Types>
|
||||
using common_comparison_category_t = typename common_comparison_category<_Types...>::type;
|
||||
inline constexpr unsigned char _Classify_category =
|
||||
_Comparison_category{(_Classify_category<_Types> | ... | _Comparison_category_strong)};
|
||||
template <class _Ty>
|
||||
inline constexpr unsigned char _Classify_category<_Ty> = _Comparison_category_none;
|
||||
template <>
|
||||
inline constexpr unsigned char _Classify_category<partial_ordering> = _Comparison_category_partial;
|
||||
template <>
|
||||
inline constexpr unsigned char _Classify_category<weak_ordering> = _Comparison_category_weak;
|
||||
template <>
|
||||
inline constexpr unsigned char _Classify_category<strong_ordering> = _Comparison_category_strong;
|
||||
|
||||
template <class... _Types>
|
||||
using common_comparison_category_t =
|
||||
conditional_t<(_Classify_category<_Types...> & _Comparison_category_none) != 0, void,
|
||||
conditional_t<(_Classify_category<_Types...> & _Comparison_category_partial) != 0, partial_ordering,
|
||||
conditional_t<(_Classify_category<_Types...> & _Comparison_category_weak) != 0, weak_ordering,
|
||||
strong_ordering>>>;
|
||||
|
||||
template <class... _Types>
|
||||
struct common_comparison_category {
|
||||
using type = common_comparison_category_t<_Types...>;
|
||||
};
|
||||
|
||||
#if defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts)
|
||||
// clang-format off
|
||||
template <class _Ty, class _Cat>
|
||||
concept _Compares_as = same_as<common_comparison_category_t<_Ty, _Cat>, _Cat>;
|
||||
|
||||
template <class _Ty, class _Cat = partial_ordering>
|
||||
concept three_way_comparable = _Half_equality_comparable<_Ty, _Ty> && _Half_ordered<_Ty, _Ty>
|
||||
&& requires(const remove_reference_t<_Ty>& __a, const remove_reference_t<_Ty>& __b) {
|
||||
{ __a <=> __b } -> _Compares_as<_Cat>;
|
||||
};
|
||||
|
||||
template <class _Ty1, class _Ty2, class _Cat = partial_ordering>
|
||||
concept three_way_comparable_with = three_way_comparable<_Ty1, _Cat> && three_way_comparable<_Ty2, _Cat>
|
||||
&& common_reference_with<const remove_reference_t<_Ty1>&, const remove_reference_t<_Ty2>&>
|
||||
&& three_way_comparable<common_reference_t<const remove_reference_t<_Ty1>&, const remove_reference_t<_Ty2>&>, _Cat>
|
||||
&& _Weakly_equality_comparable_with<_Ty1, _Ty2> && _Partially_ordered_with<_Ty1, _Ty2>
|
||||
&& requires(const remove_reference_t<_Ty1>& __t, const remove_reference_t<_Ty2>& __u) {
|
||||
{ __t <=> __u } -> _Compares_as<_Cat>;
|
||||
{ __u <=> __t } -> _Compares_as<_Cat>;
|
||||
};
|
||||
|
||||
template <class _Ty1, class _Ty2 = _Ty1>
|
||||
using compare_three_way_result_t =
|
||||
decltype(_STD declval<const remove_reference_t<_Ty1>&>() <=> _STD declval<const remove_reference_t<_Ty2>&>());
|
||||
|
||||
template <class _Ty1, class _Ty2 = _Ty1>
|
||||
struct compare_three_way_result {};
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
requires requires { typename compare_three_way_result_t<_Ty1, _Ty2>; }
|
||||
struct compare_three_way_result<_Ty1, _Ty2> {
|
||||
using type = compare_three_way_result_t<_Ty1, _Ty2>;
|
||||
};
|
||||
|
||||
struct compare_three_way {
|
||||
template <class _Ty1, class _Ty2>
|
||||
requires three_way_comparable_with<_Ty1, _Ty2> // TRANSITION, GH-489
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(_STD forward<_Ty1>(_Left) <=> _STD forward<_Ty2>(_Right))) /* strengthened */ {
|
||||
return _STD forward<_Ty1>(_Left) <=> _STD forward<_Ty2>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
// clang-format on
|
||||
#endif // defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts)
|
||||
|
||||
// Other components not yet implemented
|
||||
_STD_END
|
||||
|
|
|
@ -232,17 +232,19 @@ concept _STL_BOOLEAN_CONCEPT = movable<remove_cvref_t<_Ty>>
|
|||
|
||||
// CONCEPT _Weakly_equality_comparable_with
|
||||
template <class _Ty1, class _Ty2>
|
||||
concept _Weakly_equality_comparable_with =
|
||||
requires(const remove_reference_t<_Ty1>& __t, const remove_reference_t<_Ty2>& __u) {
|
||||
{ __t == __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __t != __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __u == __t } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __u != __t } -> _STL_BOOLEAN_CONCEPT;
|
||||
concept _Half_equality_comparable =
|
||||
requires(const remove_reference_t<_Ty1>& __x, const remove_reference_t<_Ty2>& __y) {
|
||||
{ __x == __y } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __x != __y } -> _STL_BOOLEAN_CONCEPT;
|
||||
};
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
concept _Weakly_equality_comparable_with =
|
||||
_Half_equality_comparable<_Ty1, _Ty2> && _Half_equality_comparable<_Ty2, _Ty1>;
|
||||
|
||||
// CONCEPT equality_comparable
|
||||
template <class _Ty>
|
||||
concept equality_comparable = _Weakly_equality_comparable_with<_Ty, _Ty>;
|
||||
concept equality_comparable = _Half_equality_comparable<_Ty, _Ty>;
|
||||
|
||||
// CONCEPT equality_comparable_with
|
||||
template <class _Ty1, class _Ty2>
|
||||
|
@ -251,32 +253,29 @@ concept equality_comparable_with = equality_comparable<_Ty1> && equality_compara
|
|||
&& equality_comparable<common_reference_t<const remove_reference_t<_Ty1>&, const remove_reference_t<_Ty2>&>>
|
||||
&& _Weakly_equality_comparable_with<_Ty1, _Ty2>;
|
||||
|
||||
|
||||
// CONCEPT _Partially_ordered_with
|
||||
template <class _Ty1, class _Ty2>
|
||||
concept _Half_ordered = requires(const remove_reference_t<_Ty1>& __t, const remove_reference_t<_Ty2>& __u) {
|
||||
{ __t < __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __t > __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __t <= __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __t >= __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
};
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
concept _Partially_ordered_with = _Half_ordered<_Ty1, _Ty2> && _Half_ordered<_Ty2, _Ty1>;
|
||||
|
||||
// CONCEPT totally_ordered
|
||||
template <class _Ty>
|
||||
concept totally_ordered = equality_comparable<_Ty>
|
||||
&& requires(const remove_reference_t<_Ty>& __x, const remove_reference_t<_Ty>& __y) {
|
||||
{ __x < __y } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __x > __y } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __x <= __y } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __x >= __y } -> _STL_BOOLEAN_CONCEPT;
|
||||
};
|
||||
concept totally_ordered = equality_comparable<_Ty> && _Half_ordered<_Ty, _Ty>;
|
||||
|
||||
// CONCEPT totally_ordered_with
|
||||
template <class _Ty1, class _Ty2>
|
||||
concept totally_ordered_with = totally_ordered<_Ty1> && totally_ordered<_Ty2>
|
||||
&& common_reference_with<const remove_reference_t<_Ty1>&, const remove_reference_t<_Ty2>&>
|
||||
&& totally_ordered<common_reference_t<const remove_reference_t<_Ty1>&, const remove_reference_t<_Ty2>&>>
|
||||
&& equality_comparable_with<_Ty1, _Ty2>
|
||||
&& requires(const remove_reference_t<_Ty1>& __t, const remove_reference_t<_Ty2>& __u) {
|
||||
{ __t < __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __t > __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __t <= __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __t >= __u } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __u < __t } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __u > __t } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __u <= __t } -> _STL_BOOLEAN_CONCEPT;
|
||||
{ __u >= __t } -> _STL_BOOLEAN_CONCEPT;
|
||||
};
|
||||
&& equality_comparable_with<_Ty1, _Ty2> && _Partially_ordered_with<_Ty1, _Ty2>;
|
||||
|
||||
// CONCEPT copyable
|
||||
template <class _Ty>
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#endif // _HAS_CXX17
|
||||
#ifdef __cpp_lib_concepts
|
||||
#include <compare>
|
||||
#endif // __cpp_lib_concepts
|
||||
|
||||
#pragma pack(push, _CRT_PACKING)
|
||||
#pragma warning(push, _STL_WARNING_LEVEL)
|
||||
|
@ -182,36 +185,36 @@ struct bit_not {
|
|||
// STRUCT TEMPLATE SPECIALIZATION divides
|
||||
template <>
|
||||
struct divides<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
-> decltype(_STD forward<_Ty1>(_Left) / _STD forward<_Ty2>(_Right)) {
|
||||
return _STD forward<_Ty1>(_Left) / _STD forward<_Ty2>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION modulus
|
||||
template <>
|
||||
struct modulus<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
-> decltype(_STD forward<_Ty1>(_Left) % _STD forward<_Ty2>(_Right)) {
|
||||
return _STD forward<_Ty1>(_Left) % _STD forward<_Ty2>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION negate
|
||||
template <>
|
||||
struct negate<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty>
|
||||
constexpr auto operator()(_Ty&& _Left) const -> decltype(-_STD forward<_Ty>(_Left)) {
|
||||
return -_STD forward<_Ty>(_Left);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION equal_to
|
||||
|
@ -235,83 +238,83 @@ struct negate<void> {
|
|||
// STRUCT TEMPLATE SPECIALIZATION logical_and
|
||||
template <>
|
||||
struct logical_and<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
-> decltype(_STD forward<_Ty1>(_Left) && _STD forward<_Ty2>(_Right)) {
|
||||
return _STD forward<_Ty1>(_Left) && _STD forward<_Ty2>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION logical_or
|
||||
template <>
|
||||
struct logical_or<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
-> decltype(_STD forward<_Ty1>(_Left) || _STD forward<_Ty2>(_Right)) {
|
||||
return _STD forward<_Ty1>(_Left) || _STD forward<_Ty2>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION logical_not
|
||||
template <>
|
||||
struct logical_not<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty>
|
||||
constexpr auto operator()(_Ty&& _Left) const -> decltype(!_STD forward<_Ty>(_Left)) {
|
||||
return !_STD forward<_Ty>(_Left);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION bit_and
|
||||
template <>
|
||||
struct bit_and<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
-> decltype(_STD forward<_Ty1>(_Left) & _STD forward<_Ty2>(_Right)) {
|
||||
return _STD forward<_Ty1>(_Left) & _STD forward<_Ty2>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION bit_or
|
||||
template <>
|
||||
struct bit_or<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
-> decltype(_STD forward<_Ty1>(_Left) | _STD forward<_Ty2>(_Right)) {
|
||||
return _STD forward<_Ty1>(_Left) | _STD forward<_Ty2>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION bit_xor
|
||||
template <>
|
||||
struct bit_xor<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
-> decltype(_STD forward<_Ty1>(_Left) ^ _STD forward<_Ty2>(_Right)) {
|
||||
return _STD forward<_Ty1>(_Left) ^ _STD forward<_Ty2>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION bit_not
|
||||
template <>
|
||||
struct bit_not<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty>
|
||||
constexpr auto operator()(_Ty&& _Left) const -> decltype(~_STD forward<_Ty>(_Left)) {
|
||||
return ~_STD forward<_Ty>(_Left);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
#if _HAS_DEPRECATED_NEGATORS
|
||||
|
@ -658,12 +661,12 @@ _NODISCARD _Mem_fn<_Rx _Ty::*> mem_fn(_Rx _Ty::*_Pm) noexcept {
|
|||
#if _HAS_CXX20
|
||||
// STRUCT identity
|
||||
struct identity {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty>
|
||||
_NODISCARD constexpr _Ty&& operator()(_Ty&& _Left) const noexcept {
|
||||
return _STD forward<_Ty>(_Left);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
#endif // _HAS_CXX20
|
||||
|
||||
|
@ -2198,6 +2201,66 @@ private:
|
|||
};
|
||||
#endif // _HAS_CXX17
|
||||
|
||||
#ifdef __cpp_lib_concepts
|
||||
namespace ranges {
|
||||
// STRUCT ranges::not_equal_to
|
||||
struct not_equal_to {
|
||||
// clang-format off
|
||||
template <class _Ty1, class _Ty2>
|
||||
requires equality_comparable_with<_Ty1, _Ty2> // TRANSITION, GH-489
|
||||
_NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept(
|
||||
static_cast<bool>(static_cast<_Ty1&&>(_Left) == static_cast<_Ty2&&>(_Right)))) /* strengthened */ {
|
||||
return !static_cast<bool>(static_cast<_Ty1&&>(_Left) == static_cast<_Ty2&&>(_Right));
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT ranges::greater
|
||||
struct greater {
|
||||
// clang-format off
|
||||
template <class _Ty1, class _Ty2>
|
||||
requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489
|
||||
_NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept(
|
||||
static_cast<bool>(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)))) /* strengthened */ {
|
||||
return static_cast<bool>(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left));
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT ranges::greater_equal
|
||||
struct greater_equal {
|
||||
// clang-format off
|
||||
template <class _Ty1, class _Ty2>
|
||||
requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489
|
||||
_NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept(
|
||||
static_cast<bool>(static_cast<_Ty1&&>(_Left) < static_cast<_Ty2&&>(_Right)))) /* strengthened */ {
|
||||
return !static_cast<bool>(static_cast<_Ty1&&>(_Left) < static_cast<_Ty2&&>(_Right));
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT ranges::less_equal
|
||||
struct less_equal {
|
||||
// clang-format off
|
||||
template <class _Ty1, class _Ty2>
|
||||
requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489
|
||||
_NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept(
|
||||
static_cast<bool>(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)))) /* strengthened */ {
|
||||
return !static_cast<bool>(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left));
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
} // namespace ranges
|
||||
#endif // __cpp_lib_concepts
|
||||
|
||||
#if _HAS_TR1_NAMESPACE
|
||||
namespace _DEPRECATE_TR1_NAMESPACE tr1 {
|
||||
using _STD bad_function_call;
|
||||
|
|
|
@ -2253,8 +2253,6 @@ struct owner_less<weak_ptr<_Ty>> {
|
|||
|
||||
template <>
|
||||
struct owner_less<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty, class _Uty>
|
||||
_NODISCARD bool operator()(const shared_ptr<_Ty>& _Left, const shared_ptr<_Uty>& _Right) const noexcept {
|
||||
return _Left.owner_before(_Right);
|
||||
|
@ -2274,6 +2272,8 @@ struct owner_less<void> {
|
|||
_NODISCARD bool operator()(const weak_ptr<_Ty>& _Left, const weak_ptr<_Uty>& _Right) const noexcept {
|
||||
return _Left.owner_before(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION hash
|
||||
|
|
|
@ -377,9 +377,7 @@ namespace _CXX20_DEPRECATE_REL_OPS rel_ops {
|
|||
return !(_Left < _Right);
|
||||
}
|
||||
} // namespace rel_ops
|
||||
_STD_END
|
||||
|
||||
_STD_BEGIN
|
||||
// STRUCTS FOR STRUCTURED BINDINGS tuple_size AND tuple_element
|
||||
template <class _Tuple>
|
||||
struct tuple_size;
|
||||
|
|
|
@ -155,118 +155,118 @@ struct less_equal {
|
|||
// STRUCT TEMPLATE SPECIALIZATION plus
|
||||
template <>
|
||||
struct plus<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) + static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) + static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) + static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION minus
|
||||
template <>
|
||||
struct minus<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) - static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) - static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) - static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION multiplies
|
||||
template <>
|
||||
struct multiplies<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) * static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) * static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) * static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION equal_to
|
||||
template <>
|
||||
struct equal_to<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) == static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) == static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) == static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION not_equal_to
|
||||
template <>
|
||||
struct not_equal_to<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) != static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) != static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) != static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION greater
|
||||
template <>
|
||||
struct greater<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) > static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) > static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) > static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION less
|
||||
template <>
|
||||
struct less<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) < static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) < static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) < static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION greater_equal
|
||||
template <>
|
||||
struct greater_equal<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) >= static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) >= static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) >= static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE SPECIALIZATION less_equal
|
||||
template <>
|
||||
struct less_equal<void> {
|
||||
using is_transparent = int;
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
|
||||
noexcept(noexcept(static_cast<_Ty1&&>(_Left) <= static_cast<_Ty2&&>(_Right))) // strengthened
|
||||
-> decltype(static_cast<_Ty1&&>(_Left) <= static_cast<_Ty2&&>(_Right)) {
|
||||
return static_cast<_Ty1&&>(_Left) <= static_cast<_Ty2&&>(_Right);
|
||||
}
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// FUNCTION TEMPLATE addressof
|
||||
|
|
|
@ -2899,6 +2899,34 @@ namespace ranges {
|
|||
|
||||
// VARIABLE ranges::prev
|
||||
inline constexpr _Prev_fn prev{_Not_quite_object::_Construct_tag{}};
|
||||
|
||||
// STRUCT ranges::equal_to
|
||||
struct equal_to {
|
||||
// clang-format off
|
||||
template <class _Ty1, class _Ty2>
|
||||
requires equality_comparable_with<_Ty1, _Ty2> // TRANSITION, GH-489
|
||||
_NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept(
|
||||
static_cast<bool>(static_cast<_Ty1&&>(_Left) == static_cast<_Ty2&&>(_Right)))) /* strengthened */ {
|
||||
return static_cast<bool>(static_cast<_Ty1&&>(_Left) == static_cast<_Ty2&&>(_Right));
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
|
||||
// STRUCT ranges::less
|
||||
struct less {
|
||||
// clang-format off
|
||||
template <class _Ty1, class _Ty2>
|
||||
requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489
|
||||
_NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept(
|
||||
static_cast<bool>(static_cast<_Ty1&&>(_Left) < static_cast<_Ty2&&>(_Right)))) /* strengthened */ {
|
||||
return static_cast<bool>(static_cast<_Ty1&&>(_Left) < static_cast<_Ty2&&>(_Right));
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
using is_transparent = int;
|
||||
};
|
||||
} // namespace ranges
|
||||
#endif // __cpp_lib_concepts
|
||||
|
||||
|
|
|
@ -222,6 +222,7 @@ tests\P0758R1_is_nothrow_convertible
|
|||
tests\P0768R1_spaceship_operator
|
||||
tests\P0769R2_shift_left_shift_right
|
||||
tests\P0811R3_midpoint_lerp
|
||||
tests\P0896R4_P1614R2_comparisons
|
||||
tests\P0896R4_ranges_iterator_machinery
|
||||
tests\P0896R4_ranges_range_machinery
|
||||
tests\P0896R4_ranges_to_address
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\concepts_matrix.lst
|
|
@ -0,0 +1,615 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
// Covers:
|
||||
// * three_way_comparable and three_way_comparable_with
|
||||
// * compare_three_way, compare_three_way_result, and compare_three_way_result_t
|
||||
// * ranges::equal_to, ranges::not_equal_to, ranges::less, ranges::less_equal,
|
||||
// ranges::greater, and ranges::greater_equal
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wsign-compare"
|
||||
#endif // __clang__
|
||||
|
||||
#include <cassert>
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <ranges>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
|
||||
|
||||
namespace ranges = std::ranges;
|
||||
|
||||
using std::common_comparison_category_t;
|
||||
using std::compare_three_way, std::compare_three_way_result, std::compare_three_way_result_t;
|
||||
using std::partial_ordering, std::weak_ordering, std::strong_ordering;
|
||||
using std::same_as, std::convertible_to;
|
||||
using std::three_way_comparable, std::three_way_comparable_with;
|
||||
|
||||
template <class T, class... Types>
|
||||
constexpr bool is_one_of = (same_as<T, Types> || ...);
|
||||
|
||||
struct common_comparable {
|
||||
template <class T>
|
||||
common_comparable(T&&);
|
||||
|
||||
bool operator==(common_comparable const&) const;
|
||||
strong_ordering operator<=>(common_comparable const&) const;
|
||||
};
|
||||
|
||||
struct common_incomparable {
|
||||
template <class T>
|
||||
common_incomparable(T&&);
|
||||
};
|
||||
|
||||
// Validate properties common to the concept-constrained comparison object types
|
||||
template <class T>
|
||||
constexpr bool is_trivially_constexpr() {
|
||||
STATIC_ASSERT(std::semiregular<T>);
|
||||
|
||||
// Not required, but likely portable nonetheless:
|
||||
STATIC_ASSERT(std::is_empty_v<T>);
|
||||
STATIC_ASSERT(std::is_trivial_v<T>);
|
||||
STATIC_ASSERT(std::is_trivially_copy_constructible_v<T>);
|
||||
STATIC_ASSERT(std::is_trivially_move_constructible_v<T>);
|
||||
STATIC_ASSERT(std::is_trivially_copy_assignable_v<T>);
|
||||
STATIC_ASSERT(std::is_trivially_move_assignable_v<T>);
|
||||
|
||||
// Not required to be constant expressions, but likely portable nonetheless:
|
||||
T value_initialized{};
|
||||
T copy_constructed = value_initialized;
|
||||
T move_constructed = std::move(copy_constructed);
|
||||
copy_constructed = std::move(move_constructed);
|
||||
move_constructed = copy_constructed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
STATIC_ASSERT(is_trivially_constexpr<compare_three_way>());
|
||||
STATIC_ASSERT(is_trivially_constexpr<ranges::equal_to>());
|
||||
STATIC_ASSERT(is_trivially_constexpr<ranges::not_equal_to>());
|
||||
STATIC_ASSERT(is_trivially_constexpr<ranges::less>());
|
||||
STATIC_ASSERT(is_trivially_constexpr<ranges::less_equal>());
|
||||
STATIC_ASSERT(is_trivially_constexpr<ranges::greater>());
|
||||
STATIC_ASSERT(is_trivially_constexpr<ranges::greater_equal>());
|
||||
|
||||
// Validate three_way_comparable
|
||||
template <int I, class Category>
|
||||
struct three_way_archetype {
|
||||
three_way_archetype() = delete;
|
||||
three_way_archetype(three_way_archetype const&) = delete;
|
||||
three_way_archetype& operator=(three_way_archetype const&) = delete;
|
||||
~three_way_archetype() = delete;
|
||||
// 0: not equality_comparable
|
||||
bool operator==(three_way_archetype const&) const requires(I == 0) = delete;
|
||||
bool operator==(three_way_archetype const&) const requires(I != 0);
|
||||
// 1: not totally_ordered
|
||||
bool operator<(three_way_archetype const&) const requires(I == 1) = delete;
|
||||
bool operator<(three_way_archetype const&) const requires(I != 1);
|
||||
bool operator>(three_way_archetype const&) const requires(I != 1);
|
||||
bool operator<=(three_way_archetype const&) const requires(I != 1);
|
||||
bool operator>=(three_way_archetype const&) const requires(I != 1);
|
||||
// 2: <=> isn't defined
|
||||
Category operator<=>(three_way_archetype const&) const requires(I != 2 && I != 3);
|
||||
// 3: <=> doesn't return a comparison category type
|
||||
int operator<=>(three_way_archetype const&) const requires(I == 3);
|
||||
};
|
||||
constexpr int three_way_archetype_max = 4;
|
||||
|
||||
template <class T, class Cat>
|
||||
constexpr bool test_three_way_comparable1() {
|
||||
STATIC_ASSERT(is_one_of<Cat, void, partial_ordering, weak_ordering, strong_ordering>);
|
||||
|
||||
STATIC_ASSERT(three_way_comparable<T, partial_ordering> == convertible_to<Cat, partial_ordering>);
|
||||
STATIC_ASSERT(three_way_comparable<T, weak_ordering> == convertible_to<Cat, weak_ordering>);
|
||||
STATIC_ASSERT(three_way_comparable<T, strong_ordering> == same_as<Cat, strong_ordering>);
|
||||
|
||||
STATIC_ASSERT(three_way_comparable_with<T, T, partial_ordering> == convertible_to<Cat, partial_ordering>);
|
||||
STATIC_ASSERT(three_way_comparable_with<T, T, weak_ordering> == convertible_to<Cat, weak_ordering>);
|
||||
STATIC_ASSERT(three_way_comparable_with<T, T, strong_ordering> == same_as<Cat, strong_ordering>);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <int... Is>
|
||||
constexpr bool test_three_way_comparable(std::integer_sequence<int, Is...>) {
|
||||
(test_three_way_comparable1<three_way_archetype<Is, partial_ordering>, void>(), ...);
|
||||
(test_three_way_comparable1<three_way_archetype<Is, weak_ordering>, void>(), ...);
|
||||
(test_three_way_comparable1<three_way_archetype<Is, strong_ordering>, void>(), ...);
|
||||
#ifndef __clang__ // TRANSITION, LLVM-44761 (fixed for RC2)
|
||||
STATIC_ASSERT(
|
||||
test_three_way_comparable1<three_way_archetype<three_way_archetype_max, partial_ordering>, partial_ordering>());
|
||||
STATIC_ASSERT(
|
||||
test_three_way_comparable1<three_way_archetype<three_way_archetype_max, weak_ordering>, weak_ordering>());
|
||||
STATIC_ASSERT(
|
||||
test_three_way_comparable1<three_way_archetype<three_way_archetype_max, strong_ordering>, strong_ordering>());
|
||||
#endif // TRANSITION, LLVM-44761 (fixed for RC2)
|
||||
|
||||
return true;
|
||||
}
|
||||
STATIC_ASSERT(test_three_way_comparable(std::make_integer_sequence<int, three_way_archetype_max>{}));
|
||||
|
||||
// Validate three_way_comparable_with
|
||||
// clang-format off
|
||||
|
||||
// 4: not common_reference_with
|
||||
// 5: common_reference_t is not three_way_comparable
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires ((I1 != I2 || !same_as<Cat1, Cat2>) && I1 != 4 && I2 != 4)
|
||||
struct std::common_type<three_way_archetype<I1, Cat1>, three_way_archetype<I2, Cat2>> {
|
||||
using type = conditional_t<I1 == 5 || I2 == 5, ::common_incomparable, ::common_comparable>;
|
||||
};
|
||||
|
||||
// 6: not _Weakly_equality_comparable_with
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires ((I1 != I2 || !same_as<Cat1, Cat2>) && (I1 == 6 || I2 == 6))
|
||||
bool operator==(three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&) = delete;
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires ((I1 != I2 || !same_as<Cat1, Cat2>) && I1 != 6 && I2 != 6)
|
||||
bool operator==(three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&);
|
||||
|
||||
// 7: not _Partially_ordered_with
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires ((I1 != I2 || !same_as<Cat1, Cat2>) && (I1 == 7 || I2 == 7))
|
||||
bool operator<(three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&) = delete;
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires ((I1 != I2 || !same_as<Cat1, Cat2>) && I1 != 7 && I2 != 7)
|
||||
bool operator<(three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&);
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires (I1 != I2 || !same_as<Cat1, Cat2>)
|
||||
bool operator>(three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&);
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires (I1 != I2 || !same_as<Cat1, Cat2>)
|
||||
bool operator<=(three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&);
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires (I1 != I2 || !same_as<Cat1, Cat2>)
|
||||
bool operator>=(three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&);
|
||||
|
||||
// 8: <=> isn't defined
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires ((I1 != I2 || !same_as<Cat1, Cat2>) && I1 != 8 && I1 != 9 && I2 != 8 && I2 != 9)
|
||||
common_comparison_category_t<Cat1, Cat2> operator<=>(
|
||||
three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&);
|
||||
|
||||
// 9: <=> returns a non-comparison category type
|
||||
template <int I1, class Cat1, int I2, class Cat2>
|
||||
requires ((I1 != I2 || !same_as<Cat1, Cat2>) && I1 == 9 && I2 == 9)
|
||||
int operator<=>(three_way_archetype<I1, Cat1> const&, three_way_archetype<I2, Cat2> const&);
|
||||
|
||||
// clang-format on
|
||||
|
||||
constexpr int three_way_with_max = 10;
|
||||
|
||||
template <class T, class Cat>
|
||||
constexpr bool test_three_way_comparable_with1() {
|
||||
STATIC_ASSERT(is_one_of<Cat, void, partial_ordering, weak_ordering, strong_ordering>);
|
||||
|
||||
// All specializations of three_way_archetype<I, T> for which I >= three_way_with_max are "good"; we need such a
|
||||
// specialization that is different from three_way_archetype<three_way_with_max, T> to ensure we're fully testing
|
||||
// the cross-type three_way_comparable_with concept. Why not three_way_with_max + 1?
|
||||
constexpr int three_way_known_good = three_way_with_max + 1;
|
||||
|
||||
using P = three_way_archetype<three_way_known_good, partial_ordering>;
|
||||
using W = three_way_archetype<three_way_known_good, weak_ordering>;
|
||||
using S = three_way_archetype<three_way_known_good, strong_ordering>;
|
||||
|
||||
STATIC_ASSERT(three_way_comparable_with<T, P, partial_ordering> == convertible_to<Cat, partial_ordering>);
|
||||
STATIC_ASSERT(three_way_comparable_with<T, W, weak_ordering> == convertible_to<Cat, weak_ordering>);
|
||||
STATIC_ASSERT(three_way_comparable_with<T, S, strong_ordering> == same_as<Cat, strong_ordering>);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <int... Is>
|
||||
constexpr bool test_three_way_comparable_with(std::integer_sequence<int, Is...>) {
|
||||
(test_three_way_comparable_with1<three_way_archetype<Is, partial_ordering>, void>(), ...);
|
||||
(test_three_way_comparable_with1<three_way_archetype<Is, weak_ordering>, void>(), ...);
|
||||
(test_three_way_comparable_with1<three_way_archetype<Is, strong_ordering>, void>(), ...);
|
||||
#ifndef __clang__ // TRANSITION, LLVM-44761 (fixed for RC2)
|
||||
STATIC_ASSERT(
|
||||
test_three_way_comparable_with1<three_way_archetype<three_way_with_max, partial_ordering>, partial_ordering>());
|
||||
STATIC_ASSERT(
|
||||
test_three_way_comparable_with1<three_way_archetype<three_way_with_max, weak_ordering>, weak_ordering>());
|
||||
STATIC_ASSERT(
|
||||
test_three_way_comparable_with1<three_way_archetype<three_way_with_max, strong_ordering>, strong_ordering>());
|
||||
#endif // TRANSITION, LLVM-44761 (fixed for RC2)
|
||||
|
||||
return true;
|
||||
}
|
||||
STATIC_ASSERT(test_three_way_comparable_with(std::make_integer_sequence<int, three_way_with_max>{}));
|
||||
|
||||
// Validate static properties of compare_three_way, compare_three_way_result, and compare_three_way_result_t
|
||||
template <class T>
|
||||
concept is_trait = requires {
|
||||
typename T::type;
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
concept can_three_way = requires(T const& t, U const& u) {
|
||||
t <=> u;
|
||||
};
|
||||
|
||||
template <class T, class U, class Cat>
|
||||
constexpr bool test_compare_three_way() {
|
||||
STATIC_ASSERT(same_as<T, std::remove_cvref_t<T>>);
|
||||
STATIC_ASSERT(same_as<U, std::remove_cvref_t<U>>);
|
||||
|
||||
STATIC_ASSERT(can_three_way<T, U> == !std::is_void_v<Cat>);
|
||||
STATIC_ASSERT(can_three_way<U, T> == !std::is_void_v<Cat>);
|
||||
if constexpr (can_three_way<T, U>) {
|
||||
STATIC_ASSERT(same_as<decltype(std::declval<T const&>() <=> std::declval<U const&>()), Cat>);
|
||||
STATIC_ASSERT(same_as<decltype(std::declval<U const&>() <=> std::declval<T const&>()), Cat>);
|
||||
STATIC_ASSERT(same_as<compare_three_way_result_t<T, U>, Cat>);
|
||||
STATIC_ASSERT(same_as<compare_three_way_result_t<U, T>, Cat>);
|
||||
STATIC_ASSERT(same_as<typename compare_three_way_result<T, U>::type, Cat>);
|
||||
STATIC_ASSERT(same_as<typename compare_three_way_result<U, T>::type, Cat>);
|
||||
STATIC_ASSERT(same_as<decltype(compare_three_way{}(std::declval<T const&>(), std::declval<U const&>())), Cat>);
|
||||
STATIC_ASSERT(same_as<decltype(compare_three_way{}(std::declval<U const&>(), std::declval<T const&>())), Cat>);
|
||||
} else {
|
||||
STATIC_ASSERT(!is_trait<compare_three_way_result<T, U>>);
|
||||
STATIC_ASSERT(!is_trait<compare_three_way_result<U, T>>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class some_enum { value };
|
||||
|
||||
#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-905257
|
||||
STATIC_ASSERT(test_compare_three_way<int, int, strong_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<int, long, strong_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<float, float, partial_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<float, double, partial_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<long, double, partial_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<bool, int, void>());
|
||||
|
||||
STATIC_ASSERT(test_compare_three_way<some_enum, some_enum, strong_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<some_enum, int, void>());
|
||||
|
||||
STATIC_ASSERT(test_compare_three_way<int*, int*, strong_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<int*, void*, strong_ordering>());
|
||||
|
||||
STATIC_ASSERT(test_compare_three_way<int (*)(), int (*)(), void>());
|
||||
STATIC_ASSERT(test_compare_three_way<int (*)(), void (*)(), void>());
|
||||
#endif // TRANSITION, VSO-905257
|
||||
|
||||
template <class Cat>
|
||||
struct compares_as {};
|
||||
template <class Cat1, class Cat2>
|
||||
bool operator==(compares_as<Cat1> const&, compares_as<Cat2> const&);
|
||||
template <class Cat1, class Cat2>
|
||||
common_comparison_category_t<Cat1, Cat2> operator<=>(compares_as<Cat1> const&, compares_as<Cat2> const&);
|
||||
|
||||
template <class Cat1, class Cat2>
|
||||
struct std::common_type<compares_as<Cat1>, compares_as<Cat2>> {
|
||||
using type = common_comparable;
|
||||
};
|
||||
|
||||
STATIC_ASSERT(test_compare_three_way<compares_as<partial_ordering>, compares_as<partial_ordering>, partial_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<compares_as<partial_ordering>, compares_as<weak_ordering>, partial_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<compares_as<partial_ordering>, compares_as<strong_ordering>, partial_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<compares_as<weak_ordering>, compares_as<weak_ordering>, weak_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<compares_as<weak_ordering>, compares_as<strong_ordering>, weak_ordering>());
|
||||
STATIC_ASSERT(test_compare_three_way<compares_as<strong_ordering>, compares_as<strong_ordering>, strong_ordering>());
|
||||
|
||||
// Validate dynamic properties of compare_three_way, ranges::equal_to, ranges::not_equal_to, ranges::less,
|
||||
// ranges::less_equal, ranges::greater, ranges::greater_equal
|
||||
template <class T, class U, class R>
|
||||
constexpr void assert_three_way(T const& t, U const& u, R const result) { // TRANSITION, VSO-905257
|
||||
#if !defined(__clang__) && !defined(__EDG__)
|
||||
// <=> expressions that resolve to builtin operators are incorrectly lvalues of const-qualified type on MSVC, so
|
||||
// they are rejected by std::three_way_comparable and std::three_way_comparable_with.
|
||||
if constexpr (!std::is_class_v<std::remove_cvref_t<T>> && !std::is_class_v<std::remove_cvref_t<U>>) {
|
||||
return;
|
||||
} else
|
||||
#endif // !defined(__clang__) && !defined(__EDG__)
|
||||
{
|
||||
assert(compare_three_way{}(t, u) == result);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
constexpr void test_equality_comparable(T const& t, U const& u, strong_ordering const o) {
|
||||
assert(ranges::equal_to{}(t, t));
|
||||
assert(ranges::equal_to{}(u, u));
|
||||
assert(ranges::equal_to{}(t, u) == (o == strong_ordering::equal));
|
||||
assert(ranges::equal_to{}(u, t) == (o == strong_ordering::equal));
|
||||
|
||||
assert(!ranges::not_equal_to{}(t, t));
|
||||
assert(!ranges::not_equal_to{}(u, u));
|
||||
assert(ranges::not_equal_to{}(t, u) == !(o == strong_ordering::equal));
|
||||
assert(ranges::not_equal_to{}(u, t) == !(o == strong_ordering::equal));
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
constexpr void test_totally_ordered(T const& t, U const& u, strong_ordering const o) {
|
||||
test_equality_comparable(t, u, o);
|
||||
|
||||
assert(!ranges::less{}(t, t));
|
||||
assert(!ranges::less{}(u, u));
|
||||
assert(ranges::less{}(t, u) == (o == strong_ordering::less));
|
||||
assert(ranges::less{}(u, t) == (o == strong_ordering::greater));
|
||||
|
||||
assert(!ranges::greater{}(t, t));
|
||||
assert(!ranges::greater{}(u, u));
|
||||
assert(ranges::greater{}(t, u) == (o == strong_ordering::greater));
|
||||
assert(ranges::greater{}(u, t) == (o == strong_ordering::less));
|
||||
|
||||
assert(ranges::less_equal{}(t, t));
|
||||
assert(ranges::less_equal{}(u, u));
|
||||
assert(ranges::less_equal{}(t, u) == !(o == strong_ordering::greater));
|
||||
assert(ranges::less_equal{}(u, t) == !(o == strong_ordering::less));
|
||||
|
||||
assert(ranges::greater_equal{}(t, t));
|
||||
assert(ranges::greater_equal{}(u, u));
|
||||
assert(ranges::greater_equal{}(t, u) == !(o == strong_ordering::less));
|
||||
assert(ranges::greater_equal{}(u, t) == !(o == strong_ordering::greater));
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
constexpr void test_strongly_ordered(T const& t, U const& u) {
|
||||
assert(t < u);
|
||||
test_totally_ordered(t, u, strong_ordering::less);
|
||||
|
||||
assert_three_way(t, t, strong_ordering::equal);
|
||||
assert_three_way(u, u, strong_ordering::equal);
|
||||
assert_three_way(t, u, strong_ordering::less);
|
||||
assert_three_way(u, t, strong_ordering::greater);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
constexpr void test_weakly_ordered(T const& t, U const& u, weak_ordering const o) {
|
||||
assert_three_way(t, u, o);
|
||||
|
||||
strong_ordering test_as = strong_ordering::equal;
|
||||
|
||||
if (o == weak_ordering::equivalent) {
|
||||
assert_three_way(u, t, weak_ordering::equivalent);
|
||||
test_as = strong_ordering::equal;
|
||||
} else if (o == weak_ordering::less) {
|
||||
assert_three_way(u, t, weak_ordering::greater);
|
||||
test_as = strong_ordering::less;
|
||||
} else {
|
||||
assert(o == weak_ordering::greater);
|
||||
assert_three_way(u, t, weak_ordering::less);
|
||||
test_as = strong_ordering::greater;
|
||||
}
|
||||
|
||||
test_totally_ordered(t, u, test_as);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
constexpr void test_partially_ordered(T const& t, U const& u, partial_ordering const o) {
|
||||
assert_three_way(t, u, o);
|
||||
|
||||
strong_ordering test_as = strong_ordering::equal;
|
||||
|
||||
if (o == partial_ordering::equivalent) {
|
||||
assert_three_way(u, t, partial_ordering::equivalent);
|
||||
test_as = strong_ordering::equal;
|
||||
} else if (o == partial_ordering::less) {
|
||||
assert_three_way(u, t, partial_ordering::greater);
|
||||
test_as = strong_ordering::less;
|
||||
} else if (o == partial_ordering::greater) {
|
||||
assert_three_way(u, t, partial_ordering::less);
|
||||
test_as = strong_ordering::greater;
|
||||
} else {
|
||||
assert(o == partial_ordering::unordered);
|
||||
assert_three_way(u, t, partial_ordering::unordered);
|
||||
return;
|
||||
}
|
||||
|
||||
test_totally_ordered(t, u, test_as);
|
||||
}
|
||||
|
||||
void f1() {}
|
||||
void f2() {}
|
||||
|
||||
struct base {};
|
||||
struct derived : base {};
|
||||
|
||||
enum unscoped : int {};
|
||||
enum class scoped {};
|
||||
|
||||
// TRANSITION, VSO-980378 (use numeric_limits::quiet_NaN)
|
||||
constexpr auto NaN = __builtin_nan("0");
|
||||
constexpr auto NaNf = __builtin_nanf("0");
|
||||
|
||||
constexpr void ordering_test_cases() {
|
||||
// Validate types strongly ordered by builtin <=> operators
|
||||
test_strongly_ordered(false, true); // bool (but not with other integral types)
|
||||
|
||||
test_strongly_ordered(13, 42); // integral types (but not mixed-sign)
|
||||
test_strongly_ordered(13, 42L);
|
||||
test_strongly_ordered(13L, 42);
|
||||
test_strongly_ordered(13U, 42U);
|
||||
test_strongly_ordered(13U, 42UL);
|
||||
test_strongly_ordered(13UL, 42U);
|
||||
test_strongly_ordered(13U, L'x');
|
||||
#ifdef __cpp_char8_t
|
||||
test_strongly_ordered(13U, u8'x');
|
||||
#endif // __cpp_char8_t
|
||||
test_strongly_ordered(13U, u'x');
|
||||
#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1062618
|
||||
test_strongly_ordered(13U, U'x');
|
||||
#endif // TRANSITION, VSO-1062618
|
||||
|
||||
test_strongly_ordered(scoped{13}, scoped{42});
|
||||
test_strongly_ordered(unscoped{13}, unscoped{42});
|
||||
|
||||
int const some_ints[] = {13, 42};
|
||||
test_strongly_ordered(&some_ints[0], &some_ints[1]);
|
||||
std::pair<int, int> const int_pair{13, 42};
|
||||
test_strongly_ordered(&int_pair.first, &int_pair.second);
|
||||
|
||||
derived const some_deriveds[2] = {};
|
||||
test_strongly_ordered(&some_deriveds[0], &some_deriveds[1]);
|
||||
#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163
|
||||
if (!std::is_constant_evaluated())
|
||||
#endif // TRANSITION, VSO-938163
|
||||
{
|
||||
test_strongly_ordered(static_cast<base const*>(&some_deriveds[0]), &some_deriveds[1]);
|
||||
test_strongly_ordered(&some_deriveds[0], static_cast<base const*>(&some_deriveds[1]));
|
||||
}
|
||||
|
||||
if (!std::is_constant_evaluated()) {
|
||||
test_strongly_ordered(&some_ints[0], static_cast<void const*>(&some_ints[1]));
|
||||
test_strongly_ordered(static_cast<void const*>(&some_ints[0]), &some_ints[1]);
|
||||
|
||||
std::pair<int, long> const int_long_pair{13, 42L};
|
||||
test_strongly_ordered(static_cast<void const*>(&int_long_pair.first), &int_long_pair.second);
|
||||
test_strongly_ordered(&int_long_pair.first, static_cast<void const*>(&int_long_pair.second));
|
||||
}
|
||||
|
||||
// Validate types partially ordered by builtin <=> operators
|
||||
test_partially_ordered(1.414, 3.14, partial_ordering::less);
|
||||
test_partially_ordered(1.414f, 3.14, partial_ordering::less);
|
||||
test_partially_ordered(1.414, 3.14f, partial_ordering::less);
|
||||
test_partially_ordered(31.625f, 31.625, partial_ordering::equivalent);
|
||||
test_partially_ordered(3.14, NaN, partial_ordering::unordered);
|
||||
test_partially_ordered(3.14f, NaN, partial_ordering::unordered);
|
||||
test_partially_ordered(3.14, NaNf, partial_ordering::unordered);
|
||||
|
||||
// Validate types with no builtin <=> operators that are nonetheless totally_ordered (within a
|
||||
// limited domain) or equality_comparable
|
||||
#ifndef __clang__
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4018) // '%s': signed/unsigned mismatch
|
||||
#pragma warning(disable : 4389) // '%s': signed/unsigned mismatch
|
||||
#endif // !__clang__
|
||||
test_totally_ordered(13, 42u, strong_ordering::less);
|
||||
test_totally_ordered(13u, 42, strong_ordering::less);
|
||||
#ifndef __clang__
|
||||
#pragma warning(pop)
|
||||
#endif // !__clang__
|
||||
|
||||
test_totally_ordered(3.14, 42, strong_ordering::less);
|
||||
test_totally_ordered(1, 3.14f, strong_ordering::less);
|
||||
|
||||
test_equality_comparable(&f1, &f2, strong_ordering::less); // This means "not equal"
|
||||
|
||||
struct has_members {
|
||||
int x;
|
||||
const int y;
|
||||
|
||||
int f() {
|
||||
return 13;
|
||||
}
|
||||
int g() noexcept {
|
||||
return 42;
|
||||
}
|
||||
};
|
||||
test_equality_comparable(&has_members::x, &has_members::y, strong_ordering::less); // Ditto "not equal"
|
||||
#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1070391
|
||||
test_equality_comparable(&has_members::f, &has_members::g, strong_ordering::less); // Ditto "not equal"
|
||||
#endif // TRANSITION, VSO-1070391
|
||||
|
||||
test_equality_comparable(nullptr, nullptr, strong_ordering::equal);
|
||||
|
||||
// Validate class types
|
||||
struct partially_ordered_class {
|
||||
int i;
|
||||
|
||||
constexpr explicit partially_ordered_class(int x) noexcept : i{x} {}
|
||||
|
||||
partially_ordered_class(partially_ordered_class const&) = delete;
|
||||
partially_ordered_class& operator=(partially_ordered_class const&) = delete;
|
||||
|
||||
constexpr bool operator==(partially_ordered_class const& that) const {
|
||||
if (i == 42 || that.i == 42) {
|
||||
return false;
|
||||
} else {
|
||||
return i == that.i;
|
||||
}
|
||||
}
|
||||
constexpr partial_ordering operator<=>(partially_ordered_class const& that) const {
|
||||
if (i == 42 || that.i == 42) {
|
||||
return partial_ordering::unordered;
|
||||
} else {
|
||||
return i <=> that.i;
|
||||
}
|
||||
}
|
||||
};
|
||||
struct weakly_ordered_class {
|
||||
int i;
|
||||
|
||||
constexpr explicit weakly_ordered_class(int x) noexcept : i{x} {}
|
||||
|
||||
weakly_ordered_class(weakly_ordered_class const&) = delete;
|
||||
weakly_ordered_class& operator=(weakly_ordered_class const&) = delete;
|
||||
|
||||
constexpr bool operator==(weakly_ordered_class const& that) const {
|
||||
return i / 2 == that.i / 2;
|
||||
}
|
||||
constexpr weak_ordering operator<=>(weakly_ordered_class const& that) const {
|
||||
return i / 2 <=> that.i / 2;
|
||||
}
|
||||
};
|
||||
struct strongly_ordered_class {
|
||||
int i;
|
||||
|
||||
constexpr explicit strongly_ordered_class(int x) noexcept : i{x} {}
|
||||
|
||||
strongly_ordered_class(strongly_ordered_class const&) = delete;
|
||||
strongly_ordered_class& operator=(strongly_ordered_class const&) = delete;
|
||||
|
||||
auto operator<=>(strongly_ordered_class const&) const = default;
|
||||
};
|
||||
|
||||
struct equality_comparable_class {
|
||||
int i;
|
||||
|
||||
struct boolish {
|
||||
bool b;
|
||||
constexpr operator bool() const {
|
||||
return b;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr explicit equality_comparable_class(int x) noexcept : i{x} {}
|
||||
|
||||
equality_comparable_class(equality_comparable_class const&) = delete;
|
||||
equality_comparable_class& operator=(equality_comparable_class const&) = delete;
|
||||
|
||||
constexpr boolish operator==(equality_comparable_class const& that) const {
|
||||
return {i == that.i};
|
||||
}
|
||||
constexpr boolish operator!=(equality_comparable_class const& that) const {
|
||||
return {i != that.i};
|
||||
}
|
||||
};
|
||||
|
||||
struct totally_ordered_class : equality_comparable_class {
|
||||
using equality_comparable_class::equality_comparable_class;
|
||||
|
||||
constexpr boolish operator<(totally_ordered_class const& that) const {
|
||||
return {i < that.i};
|
||||
}
|
||||
constexpr boolish operator>(totally_ordered_class const& that) const {
|
||||
return {i > that.i};
|
||||
}
|
||||
constexpr boolish operator<=(totally_ordered_class const& that) const {
|
||||
return {i <= that.i};
|
||||
}
|
||||
constexpr boolish operator>=(totally_ordered_class const& that) const {
|
||||
return {i >= that.i};
|
||||
}
|
||||
};
|
||||
|
||||
test_partially_ordered(partially_ordered_class{13}, partially_ordered_class{42}, partial_ordering::unordered);
|
||||
test_partially_ordered(partially_ordered_class{13}, partially_ordered_class{29}, partial_ordering::less);
|
||||
test_weakly_ordered(weakly_ordered_class{13}, weakly_ordered_class{42}, weak_ordering::less);
|
||||
test_weakly_ordered(weakly_ordered_class{13}, weakly_ordered_class{12}, weak_ordering::equivalent);
|
||||
test_strongly_ordered(strongly_ordered_class{13}, strongly_ordered_class{42});
|
||||
test_equality_comparable(equality_comparable_class{13}, equality_comparable_class{13}, strong_ordering::equal);
|
||||
test_equality_comparable(equality_comparable_class{13}, equality_comparable_class{42}, strong_ordering::less);
|
||||
test_totally_ordered(totally_ordered_class{13}, totally_ordered_class{42}, strong_ordering::less);
|
||||
}
|
||||
|
||||
int main() {
|
||||
STATIC_ASSERT((ordering_test_cases(), true));
|
||||
ordering_test_cases();
|
||||
}
|
Загрузка…
Ссылка в новой задаче