Basically what I want is something simple, a function:
template<typename A, typename B>
is_the_same_value(A a, B b)
{
// return true if A is the same value as B
}
This seemingly simple question is is hard as using return a == b fails for int/unsigned mix, e.g.
is_the_same_value(-1, 0xffffffff)
would return true. clang/gcc warn about this: comparison of integers of different signs
Using something like a == b && ((a > 0) == (b > 0))
works but still triggers the compiler warning.
You can use std::common_type
to remove the warning.
#include <type_traits>
template<typename A, typename B>
bool is_numbers_equal(A a, B b) {
using C = typename std::common_type<A, B>::type;
return (a > 0) == (b > 0) && static_cast<C>(a) == static_cast<C>(b);
// or maybe `... && C(a) == C(b);`
}
Well, this looks fun to optimize it for the compiler for types that both are the "same":
#include <type_traits>
template<typename A, typename B>
typename std::enable_if<
std::is_integral<A>::value &&
std::is_integral<B>::value &&
(std::is_signed<A>::value ^ std::is_signed<B>::value)
, bool>::type
is_numbers_equal(A a, B b) {
using C = typename std::common_type<A, B>::type;
return (a >= 0) == (b >= 0) &&
static_cast<C>(a) == static_cast<C>(b);
}
template<typename A, typename B>
typename std::enable_if<
! (
std::is_integral<A>::value &&
std::is_integral<B>::value &&
(std::is_signed<A>::value ^ std::is_signed<B>::value)
)
, bool>::type
is_numbers_equal(A a, B b) {
return a == b;
}
For value based comparisons where no implicit conversions (signed/unsigned etc) are allowed, checking for type equality could be an option:
#include <type_traits>
template<typename A, typename B>
bool is_the_same_value(const A& a, const B& b)
{
if constexpr(!std::is_same_v<A,B>) return false;
else return a == b;
}
Can't you use std::is_integral
to check whether both types are int? Then convert them to a signed long long
and then compare them.
You can compare memories:
template<typename A, typename B>
bool is_the_same_value(A a, B b)
{
return std::memcmp(&a,&b,sizeof(A)) == 0;
// return true if A is the same value as B
}
This does not look like a hard problem to me. There are enough complications so I would not call the result "simple", but it's far from complex. Just deal with the identified issues one at a time, and make sure things are symmetric with respect to the two parameters.
Issue 1: converting a negative value to an unsigned type is bad.
Issue 2: comparing different types can cause compiler warnings.
template<typename A, typename B>
bool is_the_same_value(A a, B b)
{
// Address issue 1 by returning false if exactly one of the parameters
// is negative.
if ( a < 0 && b >= 0 )
return false;
if ( b < 0 && a >= 0 )
return false;
// At this point, either both a and b are negative (hence A and B are signed types)
// or both are non-negative (hence converting to an unsigned type is fine).
// Address issue 2 by explicitly casting. Make sure you cast both
// ways to keep things symmetric.
return a == static_cast<A>(b) &&
b == static_cast<B>(a);
}
Sure, there are clever ways to make this code more compact (and less readable), but isn't the compiler supposed to be able to do those optimizations for you?
User contributions licensed under CC BY-SA 3.0