The C++ way isn't necessarily to stay away of such things as bit comparisons, but rather to do it in the most obvious way possible -by that I mean that your code should look dangerous and explicit at the same time. You have tools for this at your disposal: namespaces, ugly looking casts and algorithms:
#include <algorithm>
namespace unsafe {
template <typename T>
constexpr auto begin(const T& item) {
return reinterpret_cast<const char*>(&item);
}
template <typename T>
constexpr auto end(const T& item) {
return reinterpret_cast<const char*>(&item)+sizeof(T);
}
template <typename T>
auto bitwise_equal(const T& lhs, const T& rhs) {
return std::equal(begin(lhs), end(lhs), // will become constexpr with C++20
begin(rhs), end(rhs));
}
}
So it won't look innocuous when you use it:
int main() {
auto a = 125.5;
std::cout << std::boolalpha << unsafe::bitwise_equal(a, a);
}
A few further remarks:
bitwise_compare isn't the most accurate name for the function, since compare
is traditionally reserved for functions returning order (-1, 0, 1), and not only equality.
providing begin()
and end()
isn't necessary, since begin(item)+sizeof(T)
is as correct, and probably as readable a replacement, but aesthetic reasons are sometimes enough
std::equal
is set to become constexpr
with the new standard, meaning this C++'s style implementation might become more efficient than the memcmp
implementation given compile-time values.
it's generally better to use the four-argument versions of algorithms over two ranges; in this case it is obviously redundant since the length of the two arguments are by construction equal, but then consistency is a virtue.
reusing algorithm
s is a good way to avoid dumb but frequent errors one would make with a raw loop,
for instance:
auto pa = reinterpret_cast<const char*>(&a);
for (auto pb = reinterpret_cast<const char*>(&b); pb != pb+sizeof(T); ++pb) // oops!
// ...
operator==
normally. \$\endgroup\$sizeof
reports in units ofchar
, makingsizeof (char)
equal to 1 by definition, don't you? \$\endgroup\$