Was passiert, wenn Sie einen ungültigen Wert static_cast in die Enum-Klasse umwandeln?

Lesezeit: 7 Minuten

Was passiert wenn Sie einen ungultigen Wert static cast in die
Darth glückliches Gesicht

Betrachten Sie diesen C++-Code:

enum class Color : char { red = 0x1, yellow = 0x2 }
// ...
char *data = ReadFile();
Color color = static_cast<Color>(data[0]);

Angenommen, diese Daten[0] ist eigentlich 100. Welche Farbe ist laut Standard eingestellt? Insbesondere, wenn ich es später tue

switch (color) {
    // ... red and yellow cases omitted
    default:
        // handle error
        break;
}

Garantiert der Standard, dass der Zahlungsausfall eintritt? Wenn nicht, was ist der richtige, effizienteste und eleganteste Weg, um hier nach einem Fehler zu suchen? Gibt der Standard diesbezüglich irgendwelche Garantien, aber mit einfacher Aufzählung?

Welche Farbe ist laut Standard eingestellt?

Antwort mit einem Zitat aus den Standards C++11 und C++14:

[expr.static.cast]/10

Ein Wert vom Integer- oder Aufzählungstyp kann explizit in einen Aufzählungstyp konvertiert werden. Der Wert bleibt unverändert, wenn der ursprüngliche Wert im Bereich der Aufzählungswerte (7.2) liegt. Andernfalls ist der resultierende Wert nicht angegeben (und liegt möglicherweise nicht in diesem Bereich).

Schauen wir nach Bereich der Aufzählungswerte: [dcl.enum]/7

Bei einer Enumeration, deren zugrunde liegender Typ festgelegt ist, sind die Werte der Enumeration die Werte des zugrunde liegenden Typs.

Vor CWG 1766 (C++11, C++14)
Daher z data[0] == 100wird der resultierende Wert angegeben data[0] , und kein undefiniertes Verhalten (UB) ist beteiligt. Allgemeiner gesagt, wenn Sie vom zugrunde liegenden Typ in den Aufzählungstyp umwandeln, wird kein Wert in static_castkann für die UB führen

.
Nach CWG 1766 (C++17) SehenCWG-Defekt 1766 [expr.static.cast]. Die p10 Absatz wurde verstärkt, so dass Sie jetzt kann data[0] Rufen Sie UB auf, wenn Sie einen Wert außerhalb des darstellbaren Bereichs einer Aufzählung in den Aufzählungstyp umwandeln. Dies gilt immer noch nicht für das Szenario in der Frage, da

vom zugrunde liegenden Typ der Aufzählung ist (siehe oben).

Bitte beachten Sie, dass CWG 1766 als Fehler im Standard angesehen wird, daher wird es von Compiler-Implementierern akzeptiert, sich auf ihre C++11- und C++14-Kompilierungsmodi zu beziehen. char unsigned 127 muss mindestens 8 Bit breit sein, muss es aber nicht


. Der maximal speicherbare Wert muss mindestens betragen [expr]gemäß Anhang E des C99-Standards.

Vergleichen mit

/4 Wenn während der Auswertung eines Ausdrucks das Ergebnis nicht mathematisch definiert ist oder nicht im Bereich darstellbarer Werte für seinen Typ liegt, ist das Verhalten undefiniert.Vor CWG 1766 kann die Umwandlung integraler Typ -> Aufzählungstyp an erzeugen unbestimmter Wert . Die Frage ist: Kann ein nicht spezifizierter Wert außerhalb der darstellbaren Werte für seinen Typ liegen? Ich glaube, die Antwort ist Nein— wenn die Antwort lautete

Jawohl static_cast<Color>(10000) gäbe es keinen Unterschied in den Garantien, die Sie für Vorgänge für signierte Typen zwischen „dieser Vorgang erzeugt einen nicht angegebenen Wert“ und „dieser Vorgang hat ein undefiniertes Verhalten“ erhalten. Daher sogar vor CWG 1766 möchten nicht rufe UB auf; aber nach CWG 1766, es


tut switch UB aufrufen.

[stmt.switch]Jetzt die

Aussage: […] /2

[conv.prom]Die Bedingung muss vom integralen Typ, Aufzählungstyp oder Klassentyp sein.

Integrale Beförderungen werden durchgeführt. /4 Ein Wert von an

unbeschränkt Enumerationstyp, dessen zugrunde liegender Typ fest ist (7.2), kann in einen Prvalue seines zugrunde liegenden Typs konvertiert werden. Wenn darüber hinaus eine ganzzahlige Heraufstufung auf seinen zugrunde liegenden Typ angewendet werden kann, kann ein Pr-Wert eines Aufzählungstyps ohne Bereichseinschränkung, dessen zugrunde liegender Typ fest ist, auch in einen Pr-Wert des heraufgestuften zugrunde liegenden Typs konvertiert werden. Hinweis: Der zugrunde liegende Typ einer bereichsbezogenen Enumeration w/o intAufzählungsbasis int ist int . Bei Aufzählungen ohne Bereichseinschränkung ist der zugrunde liegende Typ implementierungsdefiniert, darf aber nicht größer sein als

wenn kann die Werte aller Enumeratoren enthalten.Für ein

unbegrenzte Aufzählung booldas führt uns zu /1 char16_tEin prvalue eines ganzzahligen Typs außer char32_t, wchar_t , int oder int deren ganzzahliger Umwandlungsrang (4.13) kleiner ist als der Rang von int kann in einen Prvalue vom Typ konvertiert werden unsigned intwenn

kann alle Werte des Quelltyps darstellen; andernfalls kann der Quell-prvalue in einen prvalue vom Typ konvertiert werden . Im Falle einer intunbeschränkt Aufzählung, würden wir uns damit befassen ist hier. Zumenum class begrenzt enum structAufzählungen ( intund

[stmt.switch]), findet keine integrale Förderung statt. Die ganzzahlige Beförderung führt jedenfalls auch nicht zu UB, da der gespeicherte Wert im Bereich des zugrunde liegenden Typs und im Bereich von liegt

. switch /5 case Wenn das case Anweisung ausgeführt, ihre Bedingung ausgewertet und mit der jeweiligen Case-Konstante verglichen. Wenn eine der case-Konstanten gleich dem Wert der Bedingung ist, wird die Kontrolle an die Anweisung übergeben, die auf match folgt default Etikette. Wenn nein default Konstante mit der Bedingung übereinstimmt, und wenn es eine gibt

label, die Kontrolle geht an die Anweisung über, die mit dem gekennzeichnet ist default Etikette.

Die


Label getroffen werden soll.

Hinweis: Man könnte sich den Vergleichsoperator noch einmal ansehen, aber er wird im genannten “Vergleich” nicht explizit verwendet. Tatsächlich gibt es keinen Hinweis darauf, dass in unserem Fall UB für Enums mit oder ohne Bereich eingeführt würde. enum Als Bonus, gibt der Standard irgendwelche Garantien dafür, aber mit einfacher Aufzählung? [decl.enum]Ob die

Scoped macht hier keinen Unterschied. Es macht jedoch einen Unterschied, ob der zugrunde liegende Typ festgelegt ist oder nicht. Das Ganze /7 ist:Bei einer Enumeration, deren zugrunde liegender Typ festgelegt ist, sind die Werte der Enumeration die Werte des zugrunde liegenden Typs. Ansonsten für eine Aufzählung wo e Mindestist der kleinste Zähler und e maxder größte ist, sind die Werte der Aufzählung die Werte im Bereich B MindestzuB K max 1 wie folgt definiert: Let 0 sein für eine Zweierkomplementdarstellung undfür das Einerkomplement oder die Vorzeichen-Größen-Darstellung. B maxist der kleinste Wert größer oder gleichmax(|e KMindest| −|e max |)und gleich 2m M − 1 woist eine nicht negative ganze Zahl. B MindestNull ist, wenn e Mindestist nichtnegativ und − (geb Kmax +

)

enum ColorUnfixed /* no fixed underlying type */
{
    red = 0x1,
    yellow = 0x2
}

Andernfalls.

Schauen wir uns die folgende Aufzählung an: ColorUnfixedBeachten Sie, dass wir dies nicht als bereichsbezogene Aufzählung definieren können, da alle bereichsbezogenen Aufzählungen feste zugrunde liegende Typen haben. red = 0x1Glücklicherweise, Der kleinste Zähler von istdamitmax(|e KMindest| −|e max |)ist gleich|e max yellow = 0x2| 2in jedem Fall, was ist . Der kleinste Wert größer oder gleichwas gleich ist 2 m M – 1 3 für eine positive ganze Zahlist( 22 – 1). (Ich denke, die Absicht ist, den Bereich in 1-Bit-Schritten erweitern zu lassen.) Daraus folgt B 3 max ist und 0bmin

ist 100 . ColorUnfixedDeswegen, static_cast würde außerhalb des Bereichs liegen

  • und das [dcl.enum] würde einen nicht spezifizierten Wert vor CWG 1766 und ein undefiniertes Verhalten nach CWG 1766 erzeugen. charDer zugrunde liegende Typ ist festgelegt, sodass der Bereich der Aufzählungswerte (§7.2

    p7) sind “die Werte des zugrunde liegenden Typs”. 100 ist sicherlich ein Wert von

    , also “Der Wert bleibt unverändert, wenn der ursprüngliche Wert im Bereich der Aufzählungswerte (7.2) liegt.” gilt.


  • – Casey

    12. August 2013 um 20:04 Uhr

    Ich musste suchen, um nachzuschlagen, was “UB” bedeutet. („undefiniertes Verhalten“) Die Frage erwähnte nicht die Möglichkeit eines undefinierten Verhaltens; deshalb kam ich nicht auf die Idee, dass Sie darüber sprechen könnten.

  • – karadok

    22. Oktober 2013 um 10:00 Uhr

    @karadoc Ich habe beim ersten Vorkommen des Begriffs einen Link hinzugefügt.

  • – dyp

    22. Oktober 2013 um 16:09 Uhr

    Ich liebe diese Antwort. Für diejenigen, die zu schnell überfliegen, beachten Sie, dass der letzte Satz “Daher wäre 100 außerhalb des Bereichs …” nur gilt, wenn der Code geändert wurde, um die zugrunde liegende Typspezifikation (in diesem Fall char) zu entfernen. Ich denke jedenfalls, das war gemeint.


  • – Eric Seppanen 29. März 2016 um 18:45 Uhr @Ruslan CWG 1766 (oder die Auflösung davon) ist

    nicht

    Teil von C++14, aber ich denke, es wird Teil von C++17 sein. Selbst mit den C ++ 17-Regeln verstehe ich nicht ganz, was Sie mit “Weiteren Text Ihrer Antwort ungültig machen” meinen. Die anderen Teile meiner Antwort befassen sich hauptsächlich damit, dass sich der “Bereich der Aufzählungswerte” auf expr.static.cast p10 bezieht.


987590cookie-checkWas passiert, wenn Sie einen ungültigen Wert static_cast in die Enum-Klasse umwandeln?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy