C ++函数可以安全地比较不同类型的整数?

C++ function to safely compare integers of different types?

由于隐式转换以及 std::numeric_limits<T>::max() 和朋友 return 键入 T 的事实,编写一个函数 bool cmp(IntA a, IntB b) “执行正确的事情”到概念上 return a < b;。也就是说,如果它们共享一个公共范围,则进行比较,如果不是,则确定 a 是否小于 b,而不考虑位数或符号。有没有比这个天真的实现更简单的实现?:

template <typename IntA, typename IntB>
[[nodiscard]] constexpr bool cmp(IntA a, IntB b) noexcept {
    static_assert(std::is_integral_v<IntA>);
    static_assert(sizeof(IntA) <= sizeof(long long int), "We assume we can fit everything into long long");
    static_assert(std::is_integral_v<IntB>);
    static_assert(sizeof(IntB) <= sizeof(long long int), "We assume we can fit everything into long long");
    if (a < 0) {
        if (b < 0) {
            return static_cast<signed long long int>(a) < static_cast<signed long long int>(b);
        } else {
            return true;
        }
    } else {
        if (b < 0) {
            return false;
        } else {
            return static_cast<unsigned long long int>(a) < static_cast<unsigned long long int>(b);
        }
    }
}

https://godbolt.org/z/aPbozGW9j

我们可以做些什么来确保它得到全面优化?

仅当输入的符号不匹配时,您才需要在运行时跳转(通过与零进行比较)。所以我建议使用 if constexpr 来帮助编译器仅在可能有用的情况下正确使用它。类似于:

template <typename LargestSignedInt = long long int, typename IntA, typename IntB>
[[nodiscard]] constexpr bool cmp(IntA a, IntB b) noexcept {
    static_assert(std::is_integral_v<IntA>);
    static_assert(sizeof(IntA) <= sizeof(LargestSignedInt));
    static_assert(std::is_integral_v<IntB>);
    static_assert(sizeof(IntB) <= sizeof(LargestSignedInt));

    if constexpr(sizeof(IntA) < sizeof(LargestSignedInt) && sizeof(IntB) < sizeof(LargestSignedInt))
    {
        // Both types are smaller than largest signed type supported, just use largest signed type.
        return static_cast<LargestSignedInt>(a) < static_cast<LargestSignedInt>(b);
    }

    if constexpr(std::is_signed_v<IntA> != std::is_signed_v<IntB>)
    {
        // Signedness mismatch
        if (a < 0) {
            if (b < 0) {
                return static_cast<LargestSignedInt>(a) < static_cast<LargestSignedInt>(b);
            } else {
                return true;
            }
        } else {
            if (b < 0) {
                return false;
            } else {
                return static_cast<typename std::make_unsigned<LargestSignedInt>::type>(a) < static_cast<typename std::make_unsigned<LargestSignedInt>::type>(b);
            }
        }
    }

    if constexpr(std::is_signed_v<IntA>)
    {
        // Both types are signed.
        return static_cast<LargestSignedInt>(a) < static_cast<LargestSignedInt>(b);
    }

    // Both types are unsigned.
    return static_cast<typename std::make_unsigned<LargestSignedInt>::type>(a) < static_cast<typename std::make_unsigned<LargestSignedInt>::type>(b);
}

如果您可以使用 C++20,那么您可以使用 newly added functions 来做到这一点。我们现在有

template< class T, class U >
constexpr bool cmp_equal( T t, U u ) noexcept;

template< class T, class U >
constexpr bool cmp_not_equal( T t, U u ) noexcept;

template< class T, class U >
constexpr bool cmp_less( T t, U u ) noexcept;

template< class T, class U >
constexpr bool cmp_greater( T t, U u ) noexcept;

template< class T, class U >
constexpr bool cmp_less_equal( T t, U u ) noexcept;

template< class T, class U >
constexpr bool cmp_greater_equal( T t, U u ) noexcept;

哪个

Compare the values of two integers t and u. Unlike builtin comparison operators, negative signed integers always compare less than (and not equal to) unsigned integers: the comparison is safe against lossy integer conversion.

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

It is a compile-time error if either T or U is not a signed or unsigned integer type (including standard integer type and extended integer type).

这个怎么样

template <typename IntA, typename IntB>
bool cmp(IntA a, IntB b) { 
    if constexpr (std::is_signed<IntA>::value == std::is_signed<IntB>::value)
    {
        return a < b;
    }
    return a < 0 || (b >= 0 && a < b);
}

如果两种类型具有相同的符号,那么比较它们是安全的。如果不是,则:

  1. if a < 0 then b >= 0 and we return true.
  2. 否则a >= 0。如果 b < 0 表达式将 return 为假。如果 b >= 0 那么两者都是正数并且 return a < b.
  3. 是安全的

https://godbolt.org/z/hPKxoscKn