¿Puedo reinterpretar_cast el parámetro de una función constexpr?

Sep 24 2020

Quiero escribir una función evaluada en tiempo de compilación, toma un puntero a una matriz de 4 bytes y genera un int que tiene el mismo patrón de bits que esa matriz. Entonces se me ocurrió:

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

Entonces, quiero usar f()así:

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

Sin embargo, tengo un error del compilador:

error: acceder al valor de '"GOOG"' a través de un glvalue 'int' en un caso de expresión constante f ("GOOG")
  1. ¿Cómo arreglarlo f()para que se compile?
  2. ¿Existe una mejor manera de lograr el mismo objetivo?

Respuestas

3 Quimby Sep 24 2020 at 07:10

Felicitaciones, ha activado la tarjeta de captura de aliasing estricto y su código tiene un comportamiento indefinido (si pudiera compilarse).

Hay algunos errores en su código, la versión "correcta" es:

 constexpr int f(const char* p) {
         return *reinterpret_cast<const int*>(p);
    }
  • reinterpret_castno puede desechar const.
  • cursor->p ¿error de tipografía?

Pero dado const char*que no apunta a un int, al emitirlo se rompe la estricta regla de aliasing. intno es uno de los tipos que pueden alias a otros, solo std::byte, (unsigned) charpuede.

El más limpio sería este:

#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;
    }

Pero std::memcpyno lo es constexpr, incluso en tiempo de ejecución, esto probablemente no tendrá ninguna sobrecarga, el compilador puede reconocer esto y reinterpretar los bytes por sí solo.

Así que vaya con el cambio de bits:

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);
}

Ser pedante "@@@@"tiene una longitud de 5, no de 4.