Search code examples
c++comparec++20utility

What makes some named Functions/Operators special?


Documenting on some C++20 features, i crossed some named Functions and Operators like the following under <compare> header :

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

Also under <utility>:

template< class T, class U >
constexpr bool cmp_greater_equal( T t, U u ) noexcept
{
    return !cmp_less(u, t);
}

template< class T, class U >
constexpr bool cmp_less_equal( T t, U u ) noexcept
{
    return !cmp_greater(t, u);
}

What should we know about the difference between custom comparator (or simple comparators) and these functions ? and what's special about them ?


Solution

  • The first set of functions is part of the library support for the spaceship operator <=>, added into C++20. Those utilities are there for the cases in which one finds themselves using <=> directly, instead of implicitly via the core language.

    On one foot, the way spaceship works is by returning an object which can be compared to 0, thus signalling the order relation between two objects.

    • (a <=> b) == 0 iff a == b
    • (a <=> b) < 0 iff a < b
    • etc.

    Now, if you find yourself using a <=> b directly, or handling the result object from another source, you can compare with 0 to check the relation you want. However, the resulting code is arguably not the most obvious to read.

    So those utilities are added to the library. They are there to make the code more expressive. They allow us to write is_eq(a <=> b) or is_lt(a <=> b) to query the meaning of the three way comparison. I would argue that it conveys the intent much clearer than a direct comparison with 0. Again, it's there only for the rare cases when we might need to work with the result of <=> directly.


    The other set of functions serves another purpose. When comparing integers directly, the operands undergo certain integral promotions. So the result of -1 < 0u is a somewhat unintuitive false. That is because -1 is promoted to an unsigned integer prior to the comparison (because 0u is an unsigned integer literal). This wraps around into a very large unsigned number which is of course not less than 0.

    To better support mixed comparisons, those functions were added. They examine the types involved, and give a result that is mathematically correct, given the values involved, without getting side-tracked due to value conversions that happen during integer promotion. To contrast with direct comparison via <, std::cmp_less(-1, 0u) returns true.