Kann ich den Parameter einer constexpr-Funktion neu interpretieren?
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")
- Wie kann ich
f()das Problem beheben, damit es kompiliert wird? - Gibt es einen besseren Weg, um das gleiche Ziel zu erreichen?
Antworten
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 wegwerfenconst.cursor->pTippfehler?
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.