Search code examples
c++reinterpret-caststrict-aliasing

Does reinterpret_cast<unsigned long long> of an int64_t value really break strict aliasing?


I'm attempting to write a generic version of __builtin_clz that handles all integer types, including signed ones. To ensure that conversion of signed to unsigned types doesn't change the bit representation, I decided to use reinterpret_cast.

I've got stuck on int64_t which unlike the other types doesn't seem to work with reinterpret_cast. I would think the code below is correct but it generates a warning in GCC.

#include <cstdint>

int countLeadingZeros(const std::int64_t value)
{
    static_assert(sizeof(std::int64_t) == sizeof(unsigned long long));
    return __builtin_clzll(reinterpret_cast<const unsigned long long&>(value));
}

(demo)

GCC shows a warning: dereferencing type-punned pointer will break strict-aliasing rules.

Clang compiles it without a complaint.

Which compiler is right? If it is GCC, what is the reason for the violation of strict-aliasing?


Edit: After reading the answers, I can see that the described behavior applies not only to conversion int64_t -> unsigned long long but also to long -> long long. The latter one makes the problem a little more obvious.


Solution

  • If you have a signed integer type T, you can access its value through a pointer/reference to the unsigned version of T and vice-versa.

    What you cannot do is access its value through a pointer/reference to the unsigned version of U, where U is not the original type. That's undefined behavior.

    long and long long are not the same type, no matter what the size of those types say. int64_t may be an alias for a long, a long long, or some other type. But unless you know that int64_t is an alias for signed long long (and no, testing its size is not good enough), you cannot access its value through a reference to unsigned long long.