If you attempt to perform that in a constant expression context (ie: when the compiler is forced to call your code at compile time, such as the expression leading to an array size or a template argument), you will find that it will not compile. Constexpr execution requires that anything which would provoke UB at compile time will instead be ill-formed. And since your code provokes UB (by accessing a union member which is not active), it will fail to compile.
All three major compilers will fail on this in a constant-expression context. MSVC (surprisingly) gives the best, most direct error message:
(13): error C2131: expression did not evaluate to a constant
(9): note: failure was caused by accessing a non-active member of a union
(9): note: see usage of 'foo::Foo::integer'
Your code probably "works as expected" only because you're calling it when the compiler doesn't have to execute it at compile-time. Given GCC/Clang's errors, if you used it as the array size for an array on the stack, it probably invoked GCC/Clang's variable-length array language extension. If you turned that off, your code would likely not compile. My example made it a global array, which doesn't allow VLAs.
Is there any better solution
Nope. Even C++20's bit_cast
will not be constexpr
if you provide a pointer (or a type containing pointers) as either the source or destination. Compile-time pointers are real things, not just numbers that represent addresses; converting them to/from integers is simply not a reasonable activity at compile-time.
Compile-time pointers have to point to things that exist at compile-time. There is no way to know where they will point at runtime. So the idea of hashing the compile-time value of a pointer (even a pointer to a static/global object) is just doomed from the start.