3 votes

Puis-je réinterpréter_cast le paramètre d'une fonction constexpr ?

Je veux écrire une fonction évaluée au moment de la compilation, qui prend un pointeur sur un tableau de 4 octets, et sort un int qui a la même configuration binaire que ce tableau. Donc j'ai trouvé :

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

Ensuite, je veux utiliser f() comme ça :

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

Cependant, j'ai obtenu une erreur de compilation :

error: accessing value of ‘"GOOG"’ through a ‘int’ glvalue in a constant expression case f("GOOG")
  1. Comment réparer f() pour qu'il compile ?
  2. Y a-t-il un meilleur moyen d'atteindre le même objectif ?

3voto

Quimby Points 3075

Félicitations, vous avez activé la carte piège de l'aliasing strict et votre code a un comportement indéfini (s'il compile).

Il y a quelques erreurs dans votre code, la version "correcte" est :

 constexpr int f(const char* p) {
         return *reinterpret_cast<const int*>(p);
    }
  • reinterpret_cast ne peut se défaire const .
  • cursor->p Une coquille ?

Mais comme const char* ne pointe pas vers un int le fait d'y faire un casting rompt la règle stricte de l'aliasing. int n'est pas un des types qui peuvent aliaser d'autres types - seulement std::byte, (unsigned) char peut.

Le plus propre serait celui-ci :

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

Mais std::memcpy n'est pas constexpr Même au moment de l'exécution, cela n'entraînera probablement pas de surcharge, le compilateur peut le reconnaître et réinterpréter les octets de lui-même.

Il faut donc opter pour le décalage des 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);
}

Juste pour être pédant "@@@@" a une longueur de 5 et non de 4.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X