std::cmp_eq...eater_equal(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’… std::cmp_equal, cmp_not_equal, cmp_less, cmp_greater, cmp_less_equal, cmp_greater_equal πŸ’…

okay so bestie, we're in the C++ Standard Library section of the manual (that's the manual name, mwah) and today we're talking about the single most underrated glow-up in C++20: the cmp_* family. no cap, these little icons fix the one thing that has been embarrassing signed/unsigned math since forever.


✨ NAME ✨

std::cmp_equal, cmp_not_equal, cmp_less, cmp_greater, cmp_less_equal, cmp_greater_equal β€” like, all six of them, together, holding hands 🫢


🌸 Synopsis 🌸

Defined in header <utility>:

template< class T, class U >                           (1) (since C++20)
constexpr bool cmp_equal( T t, U u ) noexcept;
template< class T, class U >                           (2) (since C++20)
constexpr bool cmp_not_equal( T t, U u ) noexcept;
template< class T, class U >                           (3) (since C++20)
constexpr bool cmp_less( T t, U u ) noexcept;
template< class T, class U >                           (4) (since C++20)
constexpr bool cmp_greater( T t, U u ) noexcept;
template< class T, class U >                           (5) (since C++20)
constexpr bool cmp_less_equal( T t, U u ) noexcept;
template< class T, class U >                           (6) (since C++20)
constexpr bool cmp_greater_equal( T t, U u ) noexcept;

So the whole vibe is: compare the values of two integers t and u. πŸ’–

But here's the tea β€” unlike builtin comparison operators, negative signed integers always compare less than (and not equal to) unsigned integers. the comparison is safe against non-value-preserving integer conversion. it's giving "finally, someone with common sense".

-1 > 0u; // true
std::cmp_greater(-1, 0u); // false

and yeah β€” it is a compile-time error if either T or U is a non-integer type, a character type, or bool. so don't even try it πŸ™ˆ


πŸŽ€ Parameters πŸŽ€

  • t β€” left-hand argument
  • u β€” right-hand argument

(like, the two things you're comparing. that's it. that's the parameter list πŸ’€)


⭐ Return value ⭐

  1. true if t is equal to u.
  2. true if t is not equal to u.
  3. true if t is less than u.
  4. true if t is greater than u.
  5. true if t is less or equal to u.
  6. true if t is greater or equal to u.

literally just yes/no energy for each of the six πŸ¦‹


🧚 Possible implementation 🧚

the "if you're nosy, here's how the sausage gets made" section. obsessed.

template<class T, class U>
constexpr bool cmp_equal(T t, U u) noexcept
{
    if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
        return t == u;
    else if constexpr (std::is_signed_v<T>)
        return t >= 0 && std::make_unsigned_t<T>(t) == u;
    else
        return u >= 0 && std::make_unsigned_t<U>(u) == t;
}

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

template<class T, class U>
constexpr bool cmp_less(T t, U u) noexcept
{
    if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
        return t < u;
    else if constexpr (std::is_signed_v<T>)
        return t < 0 || std::make_unsigned_t<T>(t) < u;
    else
        return u >= 0 && t < std::make_unsigned_t<U>(u);
}

template<class T, class U>
constexpr bool cmp_greater(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_less(u, t);
}

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

πŸ’œ Notes πŸ’œ

okay important disclaimer bestie: these functions cannot be used to compare enums (including std::byte), char, char8_t, char16_t, char32_t, wchar_t and bool. that's a hard no. βœ‹

also there's a feature-test macro if you're into that:
  • Feature-test macro: __cpp_lib_integer_comparison_functions
  • Value: 202002L
  • Std: (C++20)
  • Feature: Integer comparison functions

πŸ”₯ Example πŸ”₯

so the example below might produce a different signedness comparison warning if compiled without an appropriate warning suppression flag, e.g., -Wno-sign-compare (gcc/clang with -Wall -Wextra, see also SO: disabling a specific warning). consider yourself warned, diva.

// Run this code

#include <utility>

// Uncommenting the next line will disable "signed/unsigned comparison" warnings:
// #pragma GCC diagnostic ignored "-Wsign-compare"

int main()
{
    static_assert(sizeof(int) == 4); // precondition

    // Quite surprisingly
    static_assert(-1 > 1U); //< warning: sign-unsign comparison
    // because after implicit conversion of -1 to the RHS type (`unsigned int`)
    // the expression is equivalent to:
    static_assert(0xFFFFFFFFU > 1U);
    static_assert(0xFFFFFFFFU == static_cast<unsigned>(-1));

    // In contrast, the cmp_* family compares integers as most expected -
    // negative signed integers always compare less than unsigned integers:
    static_assert(std::cmp_less(-1, 1U));
    static_assert(std::cmp_less_equal(-1, 1U));
    static_assert(!std::cmp_greater(-1, 1U));
    static_assert(!std::cmp_greater_equal(-1, 1U));

    static_assert(-1 == 0xFFFFFFFFU); //< warning: sign-unsign comparison
    static_assert(std::cmp_not_equal(-1, 0xFFFFFFFFU));
}

like... -1 > 1U being true is the jump scare of the century and I will not be recovering 😭


🌈 See also 🌈

  • equal_to β€” function object implementing x == y (class template)
  • not_equal_to β€” function object implementing x != y (class template)
  • less β€” function object implementing x < y (class template)
  • greater β€” function object implementing x > y (class template)
  • less_equal β€” function object implementing x <= y (class template)
  • greater_equal β€” function object implementing x >= y (class template)
  • ranges::equal_to (C++20) β€” constrained function object implementing x == y (class)
  • ranges::not_equal_to (C++20) β€” constrained function object implementing x != y (class)
  • ranges::less (C++20) β€” constrained function object implementing x < y (class)
  • ranges::greater (C++20) β€” constrained function object implementing x > y (class)
  • ranges::less_equal (C++20) β€” constrained function object implementing x <= y (class)
  • ranges::greater_equal (C++20) β€” constrained function object implementing x >= y (class)
  • compare_three_way (C++20) β€” constrained function object implementing x <=> y (class)
  • in_range (C++20) β€” checks if an integer value is in the range of a given integer type (function template)
  • numeric_limits β€” provides an interface to query properties of all fundamental numeric types (class template)

that's the whole page, bestie. go forth and compare integers without fear. πŸ’…βœ¨

πŸŽ€ sign-off time: this lil masterpiece lives at http://cppreference.com, is dated 2024.06.10, and its manpage tag is std::cmp_eq...eater_equal(3) (yes, the header got aggressively truncated by the terminal and honestly, we respect the drama). ily, bye 🫢🌸
πŸ“¦ where this page comes from