<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:
Adam Bucior 2020-02-19 02:38:40 +01:00 коммит произвёл GitHub
Родитель 2b2746dd78
Коммит f01ecbbe8e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 877 добавлений и 100 удалений

Просмотреть файл

@ -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;

Просмотреть файл

@ -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();
}