Can I reinterpret_cast the parameter of a constexpr function?

I want to write a function evaluated at compile time, it takes a pointer to 4 bytes array, and outputs an int that has the same bit pattern as that array. So I came up with:

constexpr int f(const char* p) {
     return *reinterpret_cast<int*>(p);
}

Then, I want to use f() like this:

switch(x) {
case f("GOOG"):
   // do something
case f("MSFT"):
   // do something
case f("NIKE"):
  // do something
}

However, I got a compiler error:

error: accessing value of ‘"GOOG"’ through a ‘int’ glvalue in a constant expression case f("GOOG")
  1. How to fix f() so it compiles?
  2. Is there a better way to accomplish the same goal?

1 answer

  • answered 2020-09-24 07:10 Quimby

    Congratulations, you have activated the strict aliasing trap card and your code has undefined behaviour (if it would compile).

    There are few errors in your code, the "correct" version is:

     constexpr int f(const char* p) {
             return *reinterpret_cast<const int*>(p);
        }
    
    • reinterpret_cast cannot cast away const.
    • cursor->p typo?

    But since const char* does not point to an int, casting to it breaks the strict aliasing rule. int is not one of the types that can alias others - only std::byte, (unsigned) char can.

    The cleanest would be this:

    #include <cstring>
    
    constexpr int f(const char* p) {
             int val = 0;
             static_assert(sizeof(val)==4); // If the array is 4-byte long.
             std::memcpy(&val,p,sizeof val);
             return val;
        }
    
    

    But std::memcpy is not constexpr, even at run-time this will probably not have any overhead, compiler can recognize this and reinterpret the bytes on its own.

    So go with bit-shifting:

    constexpr int f(const char* p) {
           int value=0;
           using T = decltype (value);
           for(std::size_t i =0; i< sizeof(T);++i)
            value|= (T)p[i] << (8*i);
    
        return value;
        }
    
    int main(){
    
        // @ == 64
        // 1077952576 = 01000000 01000000 01000000 01000000
        static_assert(f("@@@@") ==1077952576);
    }
    

    Just to be pedantic "@@@@" has length 5, not 4.