Posso reinterpret_cast o parâmetro de uma função constexpr?

Sep 24 2020

Eu quero escrever uma função avaliada em tempo de compilação, leva um ponteiro para a matriz de 4 bytes e produz um int que tem o mesmo padrão de bits dessa matriz. Então eu vim com:

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

Então, eu quero usar f()assim:

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

No entanto, recebi um erro do compilador:

erro: acessar o valor de '"GOOG"' por meio de um glvalue 'int' em um caso de expressão constante f ("GOOG")
  1. Como consertar f()para que ele compile?
  2. Existe uma maneira melhor de atingir o mesmo objetivo?

Respostas

3 Quimby Sep 24 2020 at 07:10

Parabéns, você ativou o cartão de armadilha de aliasing estrito e seu código tem comportamento indefinido (se fosse compilar).

Existem alguns erros em seu código, a versão "correta" é:

 constexpr int f(const char* p) {
         return *reinterpret_cast<const int*>(p);
    }
  • reinterpret_castnão pode jogar fora const.
  • cursor->p erro de digitação?

Mas, como const char*não aponta para um int, a conversão para ele quebra a regra estrita de aliasing. intnão é um dos tipos que podem apelidar de outros - apenas std::byte, (unsigned) charpodem.

O mais limpo seria 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;
    }

Mas std::memcpynão é constexpr, mesmo em tempo de execução provavelmente não terá nenhuma sobrecarga, o compilador pode reconhecer isso e reinterpretar os bytes por conta própria.

Então vá com o 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);
}

Só para ser pedante "@@@@"tem comprimento 5, não 4.