Quelle est la couleur définie selon la norme?
Répondre avec une citation des standards C ++ 11 et C ++ 14:
[expr.static.cast] / 10
Une valeur de type intégral ou énumération peut être explicitement convertie en type énumération. La valeur reste inchangée si la valeur d'origine se trouve dans la plage des valeurs d'énumération (7.2). Sinon, la valeur résultante n'est pas spécifiée (et peut ne pas être dans cette plage).
Regardons la plage des valeurs d'énumération : [dcl.enum] / 7
Pour une énumération dont le type sous-jacent est fixe, les valeurs de l'énumération sont les valeurs du type sous-jacent.
Avant CWG 1766 (C ++ 11, C ++ 14)
Par conséquent, pour data[0] == 100
, la valeur résultante est spécifiée (*) et aucun comportement indéfini (UB) n'est impliqué. Plus généralement, lorsque vous transtypez du type sous-jacent au type d'énumération, aucune valeur dans data[0]
ne peut conduire à UB pour le static_cast
.
Après CWG 1766 (C ++ 17)
Voir le défaut CWG 1766 . Le paragraphe [expr.static.cast] p10 a été renforcé, donc vous pouvez maintenant appeler UB si vous transtypez une valeur qui est en dehors de la plage représentable d'une enum en type enum. Cela ne s'applique toujours pas au scénario de la question, car il data[0]
est du type sous-jacent de l'énumération (voir ci-dessus).
Veuillez noter que CWG 1766 est considéré comme un défaut dans le Standard, il est donc accepté pour les implémenteurs de compilateurs de s'appliquer à leurs modes de compilation C ++ 11 et C ++ 14.
(*) char
doit avoir une largeur d'au moins 8 bits, mais n'est pas obligatoire unsigned
. La valeur maximale stockable doit être au moins 127
conforme à l'annexe E de la norme C99.
Comparer à [expr] / 4
Si lors de l'évaluation d'une expression, le résultat n'est pas défini mathématiquement ou n'est pas dans la plage de valeurs représentables pour son type, le comportement n'est pas défini.
Avant le CWG 1766, le type intégral de conversion -> type d'énumération peut produire une valeur non spécifiée . La question est la suivante: une valeur non spécifiée peut-elle être en dehors des valeurs représentables de son type? Je crois que la réponse est non - si la réponse était oui , il n'y aurait aucune différence dans les garanties que vous obtenez pour les opérations sur les types signés entre "cette opération produit une valeur non spécifiée" et "cette opération a un comportement non défini".
Par conséquent, avant CWG 1766, même static_cast<Color>(10000)
serait pas Invoke UB; mais après CWG 1766, il fait Invoke UB.
Maintenant, la switch
déclaration:
[stmt.switch] / 2
La condition doit être de type intégral, de type énumération ou de type classe. [...] Des promotions intégrales sont effectuées.
[conv.prom] / 4
A prvalue d'un non délimité type d'énumération dont le type de sous - jacent est fixe (7,2) peut être converti en un prvalue de ce type sous - jacent. De plus, si la promotion intégrale peut être appliquée à son type sous-jacent, une prvalue d'un type d'énumération sans portée dont le type sous-jacent est fixe peut également être convertie en une prvalue du type sous-jacent promu.
Remarque: Le type sous-jacent d'une énumération étendue sans enum-base est int
. Pour les énumérations sans portée, le type sous-jacent est défini par l'implémentation, mais ne doit pas être plus grand que int
if int
peut contenir les valeurs de tous les énumérateurs.
Pour une énumération non cadrée , cela nous amène à / 1
A prvalue d'un type entier autre que bool
, char16_t
, char32_t
, ou wchar_t
dont le rang entier conversion (4,13) est inférieur au rang de int
peut être converti en un prvalue de type int
si int
peut représenter toutes les valeurs du type de source; sinon, la prvalue source peut être convertie en une prvalue de type unsigned int
.
Dans le cas d'une énumération non cadrée , nous traiterionsint
ici de l'art. Pour les énumérations étendues ( enum class
et enum struct
), aucune promotion intégrale ne s'applique. De toute façon, la promotion intégrale ne conduit pas non plus à UB, car la valeur stockée est dans la plage du type sous-jacent et dans la plage de int
.
[stmt.switch] / 5
Lorsque l' switch
instruction est exécutée, sa condition est évaluée et comparée à chaque constante de cas. Si l'une des constantes de cas est égale à la valeur de la condition, le contrôle est passé à l'instruction qui suit l' case
étiquette correspondante . Si aucune case
constante ne correspond à la condition et s'il existe une default
étiquette, le contrôle passe à l'instruction étiquetée par l' default
étiquette.
L' default
étiquette doit être frappée.
Remarque: On pourrait jeter un autre regard sur l'opérateur de comparaison, mais il n'est pas explicitement utilisé dans la "comparaison" référencée. En fait, rien n'indique que cela introduirait UB pour les énumérations à portée ou non dans notre cas.
En prime, la norme offre-t-elle des garanties à ce sujet mais avec une simple énumération?
Que la enum
portée soit ou non ne fait aucune différence ici. Cependant, le fait que le type sous-jacent soit fixe ou non fait une différence. Le [decl.enum] / 7 complet est:
Pour une énumération dont le type sous-jacent est fixe, les valeurs de l'énumération sont les valeurs du type sous-jacent. Dans le cas contraire, pour une énumération où e min est la plus petite recenseur et e max est la plus grande, les valeurs de l'énumération sont les valeurs de la plage b min à b max , définie comme suit: Soit K
être 1
une représentation de complément à deux et 0
pour une son complément ou représentation de la grandeur des signes. b max est la plus petite valeur supérieure ou égale à max (| e min | - K
, | e max |) et égale à 2M - 1 , oùM
est un entier non négatif. b min vaut zéro si e min est non négatif et - (b max + K
) sinon.
Jetons un coup d'œil à l'énumération suivante:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Notez que nous ne pouvons pas définir cela comme une énumération de portée, car toutes les énumérations de portée ont des types sous-jacents fixes.
Heureusement, ColorUnfixed
le plus petit énumérateur est red = 0x1
, donc max (| e min | - K
, | e max |) est égal à | e max | en tout cas, ce qui est yellow = 0x2
. La plus petite valeur supérieure ou égale à 2
, qui est égale à 2 M - 1 pour un entier positif M
est 3
( 2 2 - 1 ). (Je pense que l'intention est de permettre l'étendue de la plage par pas de 1 bit.) Il s'ensuit que b max est 3
et bmin est 0
.
Par conséquent, 100
serait en dehors de la plage de ColorUnfixed
, et static_cast
produirait une valeur non spécifiée avant le CWG 1766 et un comportement non défini après le CWG 1766.
char
, donc "La valeur est inchangée si la valeur d'origine est dans la plage des valeurs d'énumération (7.2)." s'applique.