Kann ich den Parameter einer constexpr-Funktion neu interpretieren?

Sep 24 2020

Ich möchte eine Funktion schreiben, die zur Kompilierungszeit ausgewertet wird. Sie benötigt einen Zeiger auf ein 4-Byte-Array und gibt ein int aus, das das gleiche Bitmuster wie dieses Array hat. Also habe ich mir Folgendes ausgedacht:

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

Dann möchte ich so verwenden f():

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

Ich habe jedoch einen Compilerfehler erhalten:

Fehler: Zugriff auf den Wert von "toget" über einen "int" -Gl-Wert in einem Fall mit konstantem Ausdruck f ("toget")
  1. Wie kann ich f()das Problem beheben, damit es kompiliert wird?
  2. Gibt es einen besseren Weg, um das gleiche Ziel zu erreichen?

Antworten

3 Quimby Sep 24 2020 at 07:10

Herzlichen Glückwunsch, Sie haben die Trap-Karte für striktes Aliasing aktiviert und Ihr Code hat ein undefiniertes Verhalten (falls er kompiliert werden würde).

Ihr Code enthält nur wenige Fehler. Die "richtige" Version lautet:

 constexpr int f(const char* p) {
         return *reinterpret_cast<const int*>(p);
    }
  • reinterpret_castkann nicht wegwerfen const.
  • cursor->p Tippfehler?

Da dies const char*jedoch nicht auf ein inthinweist, verstößt das Casting gegen die strenge Aliasing-Regel. intist nicht einer der Typen, die andere aliasen std::byte, (unsigned) charkönnen - nur können.

Das sauberste wäre das:

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

Aber std::memcpynicht constexpr, auch während der Laufzeit dieser wird wahrscheinlich keinen Overhead, Compiler kann dies erkennen und den Bytes auf seinem eigenen neu interpretieren.

Gehen Sie also mit 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);
}

Nur um pedantisch zu sein, "@@@@"hat die Länge 5, nicht 4.