17 Language support library [support]

17.11 Comparisons [cmp]

17.11.1 Header <compare> synopsis [compare.syn]

The header <compare> specifies types, objects, and functions for use primarily in connection with the three-way comparison operator.
// all freestanding namespace std { // [cmp.categories], comparison category types class partial_ordering; class weak_ordering; class strong_ordering; // named comparison functions constexpr bool is_eq (partial_ordering cmp) noexcept { return cmp == 0; } constexpr bool is_neq (partial_ordering cmp) noexcept { return cmp != 0; } constexpr bool is_lt (partial_ordering cmp) noexcept { return cmp < 0; } constexpr bool is_lteq(partial_ordering cmp) noexcept { return cmp <= 0; } constexpr bool is_gt (partial_ordering cmp) noexcept { return cmp > 0; } constexpr bool is_gteq(partial_ordering cmp) noexcept { return cmp >= 0; } // [cmp.common], common comparison category type template<class... Ts> struct common_comparison_category { using type = see below; }; template<class... Ts> using common_comparison_category_t = typename common_comparison_category<Ts...>::type; // [cmp.concept], concept three_way_comparable template<class T, class Cat = partial_ordering> concept three_way_comparable = see below; template<class T, class U, class Cat = partial_ordering> concept three_way_comparable_with = see below; // [cmp.result], result of three-way comparison template<class T, class U = T> struct compare_three_way_result; template<class T, class U = T> using compare_three_way_result_t = typename compare_three_way_result<T, U>::type; // [comparisons.three.way], class compare_three_way struct compare_three_way; // [cmp.alg], comparison algorithms inline namespace unspecified { inline constexpr unspecified strong_order = unspecified; inline constexpr unspecified weak_order = unspecified; inline constexpr unspecified partial_order = unspecified; inline constexpr unspecified compare_strong_order_fallback = unspecified; inline constexpr unspecified compare_weak_order_fallback = unspecified; inline constexpr unspecified compare_partial_order_fallback = unspecified; } }

17.11.2 Comparison category types [cmp.categories]

17.11.2.1 Preamble [cmp.categories.pre]

The types partial_ordering, weak_ordering, and strong_ordering are collectively termed the comparison category types.
Each is specified in terms of an exposition-only data member named value whose value typically corresponds to that of an enumerator from one of the following exposition-only enumerations: enum class ord { equal = 0, equivalent = equal, less = -1, greater = 1 }; // exposition only enum class ncmp { unordered = -127 }; // exposition only
[Note 1: 
The type strong_ordering corresponds to the term total ordering in mathematics.
— end note]
The relational and equality operators for the comparison category types are specified with an anonymous parameter of unspecified type.
This type shall be selected by the implementation such that these parameters can accept literal 0 as a corresponding argument.
[Example 1: 
nullptr_t meets this requirement.
— end example]
In this context, the behavior of a program that supplies an argument other than a literal 0 is undefined.
For the purposes of subclause [cmp.categories], substitutability is the property that f(a) == f(b) is true whenever a == b is true, where f denotes a function that reads only comparison-salient state that is accessible via the argument's public const members.

17.11.2.2 Class partial_ordering [cmp.partialord]

The partial_ordering type is typically used as the result type of a three-way comparison operator ([expr.spaceship]) for a type that admits all of the six two-way comparison operators ([expr.rel], [expr.eq]), for which equality need not imply substitutability, and that permits two values to be incomparable.198
namespace std { class partial_ordering { int value; // exposition only bool is_ordered; // exposition only // exposition-only constructors constexpr explicit partial_ordering(ord v) noexcept : value(int(v)), is_ordered(true) {} // exposition only constexpr explicit partial_ordering(ncmp v) noexcept : value(int(v)), is_ordered(false) {} // exposition only public: // valid values static const partial_ordering less; static const partial_ordering equivalent; static const partial_ordering greater; static const partial_ordering unordered; // comparisons friend constexpr bool operator==(partial_ordering v, unspecified) noexcept; friend constexpr bool operator==(partial_ordering v, partial_ordering w) noexcept = default; friend constexpr bool operator< (partial_ordering v, unspecified) noexcept; friend constexpr bool operator> (partial_ordering v, unspecified) noexcept; friend constexpr bool operator<=(partial_ordering v, unspecified) noexcept; friend constexpr bool operator>=(partial_ordering v, unspecified) noexcept; friend constexpr bool operator< (unspecified, partial_ordering v) noexcept; friend constexpr bool operator> (unspecified, partial_ordering v) noexcept; friend constexpr bool operator<=(unspecified, partial_ordering v) noexcept; friend constexpr bool operator>=(unspecified, partial_ordering v) noexcept; friend constexpr partial_ordering operator<=>(partial_ordering v, unspecified) noexcept; friend constexpr partial_ordering operator<=>(unspecified, partial_ordering v) noexcept; }; // valid values' definitions inline constexpr partial_ordering partial_ordering::less(ord::less); inline constexpr partial_ordering partial_ordering::equivalent(ord::equivalent); inline constexpr partial_ordering partial_ordering::greater(ord::greater); inline constexpr partial_ordering partial_ordering::unordered(ncmp::unordered); }
constexpr bool operator==(partial_ordering v, unspecified) noexcept; constexpr bool operator< (partial_ordering v, unspecified) noexcept; constexpr bool operator> (partial_ordering v, unspecified) noexcept; constexpr bool operator<=(partial_ordering v, unspecified) noexcept; constexpr bool operator>=(partial_ordering v, unspecified) noexcept;
Returns: For operator@, v.is_ordered && v.value @ 0.
constexpr bool operator< (unspecified, partial_ordering v) noexcept; constexpr bool operator> (unspecified, partial_ordering v) noexcept; constexpr bool operator<=(unspecified, partial_ordering v) noexcept; constexpr bool operator>=(unspecified, partial_ordering v) noexcept;
Returns: For operator@, v.is_ordered && 0 @ v.value.
constexpr partial_ordering operator<=>(partial_ordering v, unspecified) noexcept;
Returns: v.
constexpr partial_ordering operator<=>(unspecified, partial_ordering v) noexcept;
Returns: v < 0 ? partial_ordering​::​greater : v > 0 ? partial_ordering​::​less : v.
198)198)
That is, a < b, a == b, and a > b might all be false.

17.11.2.3 Class weak_ordering [cmp.weakord]

The weak_ordering type is typically used as the result type of a three-way comparison operator ([expr.spaceship]) for a type that admits all of the six two-way comparison operators ([expr.rel], [expr.eq]) and for which equality need not imply substitutability.
namespace std { class weak_ordering { int value; // exposition only // exposition-only constructors constexpr explicit weak_ordering(ord v) noexcept : value(int(v)) {} // exposition only public: // valid values static const weak_ordering less; static const weak_ordering equivalent; static const weak_ordering greater; // conversions constexpr operator partial_ordering() const noexcept; // comparisons friend constexpr bool operator==(weak_ordering v, unspecified) noexcept; friend constexpr bool operator==(weak_ordering v, weak_ordering w) noexcept = default; friend constexpr bool operator< (weak_ordering v, unspecified) noexcept; friend constexpr bool operator> (weak_ordering v, unspecified) noexcept; friend constexpr bool operator<=(weak_ordering v, unspecified) noexcept; friend constexpr bool operator>=(weak_ordering v, unspecified) noexcept; friend constexpr bool operator< (unspecified, weak_ordering v) noexcept; friend constexpr bool operator> (unspecified, weak_ordering v) noexcept; friend constexpr bool operator<=(unspecified, weak_ordering v) noexcept; friend constexpr bool operator>=(unspecified, weak_ordering v) noexcept; friend constexpr weak_ordering operator<=>(weak_ordering v, unspecified) noexcept; friend constexpr weak_ordering operator<=>(unspecified, weak_ordering v) noexcept; }; // valid values' definitions inline constexpr weak_ordering weak_ordering::less(ord::less); inline constexpr weak_ordering weak_ordering::equivalent(ord::equivalent); inline constexpr weak_ordering weak_ordering::greater(ord::greater); }
constexpr operator partial_ordering() const noexcept;
Returns: value == 0 ? partial_ordering::equivalent : value < 0 ? partial_ordering::less : partial_ordering::greater
constexpr bool operator==(weak_ordering v, unspecified) noexcept; constexpr bool operator< (weak_ordering v, unspecified) noexcept; constexpr bool operator> (weak_ordering v, unspecified) noexcept; constexpr bool operator<=(weak_ordering v, unspecified) noexcept; constexpr bool operator>=(weak_ordering v, unspecified) noexcept;
Returns: v.value @ 0 for operator@.
constexpr bool operator< (unspecified, weak_ordering v) noexcept; constexpr bool operator> (unspecified, weak_ordering v) noexcept; constexpr bool operator<=(unspecified, weak_ordering v) noexcept; constexpr bool operator>=(unspecified, weak_ordering v) noexcept;
Returns: 0 @ v.value for operator@.
constexpr weak_ordering operator<=>(weak_ordering v, unspecified) noexcept;
Returns: v.
constexpr weak_ordering operator<=>(unspecified, weak_ordering v) noexcept;
Returns: v < 0 ? weak_ordering​::​greater : v > 0 ? weak_ordering​::​less : v.

17.11.2.4 Class strong_ordering [cmp.strongord]

The strong_ordering type is typically used as the result type of a three-way comparison operator ([expr.spaceship]) for a type that admits all of the six two-way comparison operators ([expr.rel], [expr.eq]) and for which equality does imply substitutability.
namespace std { class strong_ordering { int value; // exposition only // exposition-only constructors constexpr explicit strong_ordering(ord v) noexcept : value(int(v)) {} // exposition only public: // valid values static const strong_ordering less; static const strong_ordering equal; static const strong_ordering equivalent; static const strong_ordering greater; // conversions constexpr operator partial_ordering() const noexcept; constexpr operator weak_ordering() const noexcept; // comparisons friend constexpr bool operator==(strong_ordering v, unspecified) noexcept; friend constexpr bool operator==(strong_ordering v, strong_ordering w) noexcept = default; friend constexpr bool operator< (strong_ordering v, unspecified) noexcept; friend constexpr bool operator> (strong_ordering v, unspecified) noexcept; friend constexpr bool operator<=(strong_ordering v, unspecified) noexcept; friend constexpr bool operator>=(strong_ordering v, unspecified) noexcept; friend constexpr bool operator< (unspecified, strong_ordering v) noexcept; friend constexpr bool operator> (unspecified, strong_ordering v) noexcept; friend constexpr bool operator<=(unspecified, strong_ordering v) noexcept; friend constexpr bool operator>=(unspecified, strong_ordering v) noexcept; friend constexpr strong_ordering operator<=>(strong_ordering v, unspecified) noexcept; friend constexpr strong_ordering operator<=>(unspecified, strong_ordering v) noexcept; }; // valid values' definitions inline constexpr strong_ordering strong_ordering::less(ord::less); inline constexpr strong_ordering strong_ordering::equal(ord::equal); inline constexpr strong_ordering strong_ordering::equivalent(ord::equivalent); inline constexpr strong_ordering strong_ordering::greater(ord::greater); }
constexpr operator partial_ordering() const noexcept;
Returns: value == 0 ? partial_ordering::equivalent : value < 0 ? partial_ordering::less : partial_ordering::greater
constexpr operator weak_ordering() const noexcept;
Returns: value == 0 ? weak_ordering::equivalent : value < 0 ? weak_ordering::less : weak_ordering::greater
constexpr bool operator==(strong_ordering v, unspecified) noexcept; constexpr bool operator< (strong_ordering v, unspecified) noexcept; constexpr bool operator> (strong_ordering v, unspecified) noexcept; constexpr bool operator<=(strong_ordering v, unspecified) noexcept; constexpr bool operator>=(strong_ordering v, unspecified) noexcept;
Returns: v.value @ 0 for operator@.
constexpr bool operator< (unspecified, strong_ordering v) noexcept; constexpr bool operator> (unspecified, strong_ordering v) noexcept; constexpr bool operator<=(unspecified, strong_ordering v) noexcept; constexpr bool operator>=(unspecified, strong_ordering v) noexcept;
Returns: 0 @ v.value for operator@.
constexpr strong_ordering operator<=>(strong_ordering v, unspecified) noexcept;
Returns: v.
constexpr strong_ordering operator<=>(unspecified, strong_ordering v) noexcept;
Returns: v < 0 ? strong_ordering​::​greater : v > 0 ? strong_ordering​::​less : v.

17.11.3 Class template common_comparison_category [cmp.common]

The type common_comparison_category provides an alias for the strongest comparison category to which all of the template arguments can be converted.
[Note 1: 
A comparison category type is stronger than another if they are distinct types and an instance of the former can be converted to an instance of the latter.
— end note]
template<class... Ts> struct common_comparison_category { using type = see below; };
Remarks: The member typedef-name type denotes the common comparison type ([class.spaceship]) of Ts..., the expanded parameter pack, or void if any element of Ts is not a comparison category type.
[Note 2: 
This is std​::​strong_ordering if the expansion is empty.
— end note]

17.11.4 Concept three_way_comparable [cmp.concept]

template<class T, class Cat> concept compares-as = // exposition only same_as<common_comparison_category_t<T, Cat>, Cat>; template<class T, class U> concept partially-ordered-with = // exposition only requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u) { { t < u } -> boolean-testable; { t > u } -> boolean-testable; { t <= u } -> boolean-testable; { t >= u } -> boolean-testable; { u < t } -> boolean-testable; { u > t } -> boolean-testable; { u <= t } -> boolean-testable; { u >= t } -> boolean-testable; };
Let t and u be lvalues of types const remove_reference_t<T> and const remove_reference_t<U>, respectively.
T and U model partially-ordered-with<T, U> only if:
  • t < u, t <= u, t > u, t >= u, u < t, u <= t, u > t, and u >= t have the same domain.
  • bool(t < u) == bool(u > t) is true,
  • bool(u < t) == bool(t > u) is true,
  • bool(t <= u) == bool(u >= t) is true, and
  • bool(u <= t) == bool(t >= u) is true.
template<class T, class Cat = partial_ordering> concept three_way_comparable = weakly-equality-comparable-with<T, T> && partially-ordered-with<T, T> && requires(const remove_reference_t<T>& a, const remove_reference_t<T>& b) { { a <=> b } -> compares-as<Cat>; };
Let a and b be lvalues of type const remove_reference_t<T>.
T and Cat model three_way_comparable<T, Cat> only if:
  • (a <=> b == 0) == bool(a == b) is true,
  • (a <=> b != 0) == bool(a != b) is true,
  • ((a <=> b) <=> 0) and (0 <=> (b <=> a)) are equal,
  • (a <=> b < 0) == bool(a < b) is true,
  • (a <=> b > 0) == bool(a > b) is true,
  • (a <=> b <= 0) == bool(a <= b) is true,
  • (a <=> b >= 0) == bool(a >= b) is true, and
  • if Cat is convertible to strong_ordering, T models totally_ordered ([concept.totallyordered]).
template<class T, class U, class Cat = partial_ordering> concept three_way_comparable_with = three_way_comparable<T, Cat> && three_way_comparable<U, Cat> && comparison-common-type-with<T, U> && three_way_comparable< common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>, Cat> && weakly-equality-comparable-with<T, U> && partially-ordered-with<T, U> && requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u) { { t <=> u } -> compares-as<Cat>; { u <=> t } -> compares-as<Cat>; };
Let t and t2 be lvalues denoting distinct equal objects of types const remove_reference_t<T> and remove_cvref_t<T>, respectively, and let u and u2 be lvalues denoting distinct equal objects of types const remove_reference_t<U> and remove_cvref_t<U>, respectively.
Let C be common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>.
Let CONVERT_TO_LVALUE<C>(E) be defined as in [concepts.compare.general].
T, U, and Cat model three_way_comparable_with<T, U, Cat> only if:
  • t <=> u and u <=> t have the same domain,
  • ((t <=> u) <=> 0) and (0 <=> (u <=> t)) are equal,
  • (t <=> u == 0) == bool(t == u) is true,
  • (t <=> u != 0) == bool(t != u) is true,
  • Cat(t <=> u) == Cat(CONVERT_TO_LVALUE<C>(t2) <=> CONVERT_TO_LVALUE<C>(u2)) is true,
  • (t <=> u < 0) == bool(t < u) is true,
  • (t <=> u > 0) == bool(t > u) is true,
  • (t <=> u <= 0) == bool(t <= u) is true,
  • (t <=> u >= 0) == bool(t >= u) is true, and
  • if Cat is convertible to strong_ordering, T and U model totally_ordered_with<T, U> ([concept.totallyordered]).

17.11.5 Result of three-way comparison [cmp.result]

The behavior of a program that adds specializations for the compare_three_way_result template defined in this subclause is undefined.
For the compare_three_way_result type trait applied to the types T and U, let t and u denote lvalues of types const remove_reference_t<T> and const remove_reference_t<U>, respectively.
If the expression t <=> u is well-formed when treated as an unevaluated operand ([expr.context]), the member typedef-name type denotes the type decltype(t <=> u).
Otherwise, there is no member type.

17.11.6 Comparison algorithms [cmp.alg]

The name strong_order denotes a customization point object ([customization.point.object]).
Given subexpressions E and F, the expression strong_order(E, F) is expression-equivalent ([defns.expression.equivalent]) to the following:
  • If the decayed types of E and F differ, strong_order(E, F) is ill-formed.
  • Otherwise, strong_ordering(strong_order(E, F)) if it is a well-formed expression where the meaning of strong_order is established as-if by performing argument-dependent lookup only ([basic.lookup.argdep]).
  • Otherwise, if the decayed type T of E is a floating-point type, yields a value of type strong_ordering that is consistent with the ordering observed by T's comparison operators, and if numeric_limits<T>​::​is_iec559 is true, is additionally consistent with the totalOrder operation as specified in ISO/IEC/IEEE 60559.
  • Otherwise, strong_ordering(compare_three_way()(E, F)) if it is a well-formed expression.
  • Otherwise, strong_order(E, F) is ill-formed.
[Note 1: 
Ill-formed cases above result in substitution failure when strong_order(E, F) appears in the immediate context of a template instantiation.
— end note]
The name weak_order denotes a customization point object ([customization.point.object]).
Given subexpressions E and F, the expression weak_order(E, F) is expression-equivalent ([defns.expression.equivalent]) to the following:
  • If the decayed types of E and F differ, weak_order(E, F) is ill-formed.
  • Otherwise, weak_ordering(weak_order(E, F)) if it is a well-formed expression where the meaning of weak_order is established as-if by performing argument-dependent lookup only ([basic.lookup.argdep]).
  • Otherwise, if the decayed type T of E is a floating-point type, yields a value of type weak_ordering that is consistent with the ordering observed by T's comparison operators and strong_order, and if numeric_limits<T>​::​is_iec559 is true, is additionally consistent with the following equivalence classes, ordered from lesser to greater:
  • Otherwise, weak_ordering(compare_three_way()(E, F)) if it is a well-formed expression.
  • Otherwise, weak_ordering(strong_order(E, F)) if it is a well-formed expression.
  • Otherwise, weak_order(E, F) is ill-formed.
[Note 2: 
Ill-formed cases above result in substitution failure when weak_order(E, F) appears in the immediate context of a template instantiation.
— end note]
The name partial_order denotes a customization point object ([customization.point.object]).
Given subexpressions E and F, the expression partial_order(E, F) is expression-equivalent ([defns.expression.equivalent]) to the following:
  • If the decayed types of E and F differ, partial_order(E, F) is ill-formed.
  • Otherwise, partial_ordering(partial_order(E, F)) if it is a well-formed expression where the meaning of partial_order is established as-if by performing argument-dependent lookup only ([basic.lookup.argdep]).
  • Otherwise, partial_ordering(compare_three_way()(E, F)) if it is a well-formed expression.
  • Otherwise, partial_ordering(weak_order(E, F)) if it is a well-formed expression.
  • Otherwise, partial_order(E, F) is ill-formed.
[Note 3: 
Ill-formed cases above result in substitution failure when partial_order(E, F) appears in the immediate context of a template instantiation.
— end note]
The name compare_strong_order_fallback denotes a customization point object ([customization.point.object]).
Given subexpressions E and F, the expression compare_strong_order_fallback(E, F) is expression-equivalent ([defns.expression.equivalent]) to:
  • If the decayed types of E and F differ, compare_strong_order_fallback(E, F) is ill-formed.
  • Otherwise, strong_order(E, F) if it is a well-formed expression.
  • Otherwise, if the expressions E == F and E < F are both well-formed and each of decltype(E == F) and decltype(E < F) models boolean-testable, E == F ? strong_ordering::equal : E < F ? strong_ordering::less : strong_ordering::greater except that E and F are evaluated only once.
  • Otherwise, compare_strong_order_fallback(E, F) is ill-formed.
[Note 4: 
Ill-formed cases above result in substitution failure when compare_strong_order_fallback(E, F) appears in the immediate context of a template instantiation.
— end note]
The name compare_weak_order_fallback denotes a customization point object ([customization.point.object]).
Given subexpressions E and F, the expression compare_weak_order_fallback(E, F) is expression-equivalent ([defns.expression.equivalent]) to:
  • If the decayed types of E and F differ, compare_weak_order_fallback(E, F) is ill-formed.
  • Otherwise, weak_order(E, F) if it is a well-formed expression.
  • Otherwise, if the expressions E == F and E < F are both well-formed and each of decltype(E == F) and decltype(E < F) models boolean-testable, E == F ? weak_ordering::equivalent : E < F ? weak_ordering::less : weak_ordering::greater except that E and F are evaluated only once.
  • Otherwise, compare_weak_order_fallback(E, F) is ill-formed.
[Note 5: 
Ill-formed cases above result in substitution failure when compare_weak_order_fallback(E, F) appears in the immediate context of a template instantiation.
— end note]
The name compare_partial_order_fallback denotes a customization point object ([customization.point.object]).
Given subexpressions E and F, the expression compare_partial_order_fallback(E, F) is expression-equivalent ([defns.expression.equivalent]) to:
  • If the decayed types of E and F differ, compare_partial_order_fallback(E, F) is ill-formed.
  • Otherwise, partial_order(E, F) if it is a well-formed expression.
  • Otherwise, if the expressions E == F, E < F, and F < E are all well-formed and each of decltype(E == F) and decltype(E < F) models boolean-testable, E == F ? partial_ordering::equivalent : E < F ? partial_ordering::less : F < E ? partial_ordering::greater : partial_ordering::unordered except that E and F are evaluated only once.
  • Otherwise, compare_partial_order_fallback(E, F) is ill-formed.
[Note 6: 
Ill-formed cases above result in substitution failure when compare_partial_order_fallback(E, F) appears in the immediate context of a template instantiation.
— end note]