¿A qué se ajusta el color según el estándar?
Respondiendo con una cita de los estándares C ++ 11 y C ++ 14:
[expr.static.cast] / 10
Un valor de tipo integral o de enumeración puede convertirse explícitamente en un tipo de enumeración. El valor no cambia si el valor original está dentro del rango de los valores de enumeración (7.2). De lo contrario, el valor resultante no está especificado (y podría no estar en ese rango).
Busquemos el rango de los valores de enumeración : [dcl.enum] / 7
Para una enumeración cuyo tipo subyacente es fijo, los valores de la enumeración son los valores del tipo subyacente.
Antes de CWG 1766 (C ++ 11, C ++ 14)
Por lo tanto, para data[0] == 100
, el valor resultante se especifica (*), y no está involucrado ningún comportamiento indefinido (UB) . De manera más general, a medida que se pasa del tipo subyacente al tipo de enumeración, ningún valor data[0]
puede conducir a UB para el static_cast
.
Después de CWG 1766 (C ++ 17)
Ver defecto de CWG 1766 . El párrafo [expr.static.cast] p10 se ha fortalecido, por lo que ahora puede invocar UB si lanza un valor que está fuera del rango representable de una enumeración al tipo de enumeración. Esto todavía no se aplica al escenario en la pregunta, ya que data[0]
es del tipo subyacente de la enumeración (ver arriba).
Tenga en cuenta que CWG 1766 se considera un defecto en el Estándar, por lo tanto, se acepta que los implementadores del compilador apliquen a sus modos de compilación C ++ 11 y C ++ 14.
(*) char
se requiere que tenga al menos 8 bits de ancho, pero no se requiere que sea unsigned
. Se requiere que el valor máximo almacenable sea al menos 127
según el Anexo E de la Norma C99.
Comparar con [expr] / 4
Si durante la evaluación de una expresión, el resultado no está matemáticamente definido o no está en el rango de valores representables para su tipo, el comportamiento es indefinido.
Antes de CWG 1766, el tipo integral de conversión -> tipo de enumeración puede producir un valor no especificado . La pregunta es: ¿Puede un valor no especificado estar fuera de los valores representables para su tipo? Creo que la respuesta es no . Si la respuesta fuera afirmativa , no habría ninguna diferencia en las garantías que obtiene para operaciones en tipos con signo entre "esta operación produce un valor no especificado" y "esta operación tiene un comportamiento indefinido".
Por lo tanto, antes de CWG 1766, incluso static_cast<Color>(10000)
haría no invoke UB; pero después de GTC 1766, se hace invocación UB.
Ahora, la switch
declaración:
[interruptor stmt] / 2
La condición será de tipo integral, tipo de enumeración o tipo de clase. [...] Se realizan promociones integrales.
[conv.prom] / 4
A prvalue de un sin ámbito tipo de enumeración cuyo subyacente tipo se fija (7.2) se puede convertir a un prvalue de su tipo subyacente. Además, si la promoción integral se puede aplicar a su tipo subyacente, un valor de un tipo de enumeración sin ámbito cuyo tipo subyacente es fijo también se puede convertir en un valor del tipo subyacente promocionado.
Nota: El tipo subyacente de una enumeración con ámbito sin base enum es int
. Para enumeraciones sin ámbito, el tipo subyacente está definido por la implementación, pero no debe ser mayor que int
si int
puede contener los valores de todos los enumeradores.
Para una enumeración sin ámbito , esto nos lleva a / 1
A prvalue de un tipo entero distinto de bool
, char16_t
, char32_t
, o wchar_t
cuyo número entero de conversión de rango (4,13) es menor que el rango de int
se puede convertir en un prvalue de tipo int
si int
puede representar todos los valores del tipo de fuente; de lo contrario, el valor de origen puede convertirse en un valor de tipo unsigned int
.
En el caso de un sin ámbito enumeración, que se trataría de int
s aquí. Para las enumeraciones de ámbito ( enum class
y enum struct
), no se aplica ninguna promoción integral. De cualquier manera, la promoción integral tampoco conduce a UB, ya que el valor almacenado está en el rango del tipo subyacente y en el rango de int
.
[interruptor stmt] / 5
Cuando switch
se ejecuta la instrucción, su condición se evalúa y se compara con cada constante de caso. Si una de las constantes de caso es igual al valor de la condición, el control se pasa a la instrucción que sigue a la case
etiqueta coincidente . Si ninguna case
constante coincide con la condición, y si hay una default
etiqueta, el control pasa a la declaración etiquetada por la default
etiqueta.
La default
etiqueta debe ser golpeada.
Nota: Se podría echar otro vistazo al operador de comparación, pero no se usa explícitamente en la "comparación" mencionada. De hecho, no hay indicios de que introduciría UB para enumeraciones con o sin ámbito en nuestro caso.
Como beneficio adicional, ¿el estándar ofrece alguna garantía sobre esto pero con una enumeración simple?
Si el enum
alcance es o no no hace ninguna diferencia aquí. Sin embargo, hace una diferencia si el tipo subyacente es fijo o no. El [decl.enum] / 7 completo es:
Para una enumeración cuyo tipo subyacente es fijo, los valores de la enumeración son los valores del tipo subyacente. De lo contrario, para una enumeración donde e min es el más pequeño empadronador y e max es el más grande, los valores de la enumeración son los valores en el intervalo b min a b max , definido como sigue: Sea K
sea 1
para la representación de un complemento a dos y 0
para una complemento de uno o representación de magnitud de signo. b max es el valor más pequeño mayor o igual que max (| e min | - K
, | e max |) e igual a 2M - 1 , dondeM
es un entero no negativo. b min es cero si e min no es negativo y - (b max + K
) de lo contrario.
Echemos un vistazo a la siguiente enumeración:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Tenga en cuenta que no podemos definir esto como una enumeración de ámbito, ya que todas las enumeraciones de ámbito tienen tipos subyacentes fijos.
Afortunadamente, ColorUnfixed
el enumerador más pequeño es red = 0x1
, entonces max (| e min | - K
, | e max |) es igual a | e max | en cualquier caso, que es yellow = 0x2
. El valor más pequeño mayor o igual a 2
, que es igual a 2 M - 1 para un entero positivo M
es 3
( 2 2 - 1 ). (Creo que la intención es permitir que el rango se extienda en pasos de 1 bit). Se deduce que b max es 3
y bmin es 0
.
Por 100
lo tanto, estaría fuera del rango de ColorUnfixed
, y static_cast
produciría un valor no especificado antes de CWG 1766 y un comportamiento indefinido después de CWG 1766.
char
, entonces "El valor no cambia si el valor original está dentro del rango de los valores de enumeración (7.2)". aplica.