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);
}
如果两种类型具有相同的符号,那么比较它们是安全的。如果不是,则:
- if
a < 0
then b >= 0
and we return true.
- 否则
a >= 0
。如果 b < 0
表达式将 return 为假。如果 b >= 0
那么两者都是正数并且 return a < b
. 是安全的
由于隐式转换以及 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
andu
. 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
orU
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);
}
如果两种类型具有相同的符号,那么比较它们是安全的。如果不是,则:
- if
a < 0
thenb >= 0
and we return true. - 否则
a >= 0
。如果b < 0
表达式将 return 为假。如果b >= 0
那么两者都是正数并且 returna < b
. 是安全的