Qual é a cor definida de acordo com o padrão?
Respondendo com uma citação dos padrões C ++ 11 e C ++ 14:
[expr.static.cast] / 10
Um valor do tipo integral ou de enumeração pode ser explicitamente convertido em um tipo de enumeração. O valor é inalterado se o valor original estiver dentro do intervalo dos valores de enumeração (7.2). Caso contrário, o valor resultante não é especificado (e pode não estar nesse intervalo).
Vamos procurar o intervalo dos valores de enumeração : [dcl.enum] / 7
Para uma enumeração cujo tipo subjacente é fixo, os valores da enumeração são os valores do tipo subjacente.
Antes do CWG 1766 (C ++ 11, C ++ 14)
Portanto, para data[0] == 100
, o valor resultante é especificado (*) e nenhum comportamento indefinido (UB) está envolvido. De maneira mais geral, à medida que você converte do tipo subjacente para o tipo de enumeração, nenhum valor em data[0]
pode levar a UB para o static_cast
.
Após o CWG 1766 (C ++ 17)
Consulte o defeito 1766 do CWG . O parágrafo [expr.static.cast] p10 foi reforçado, agora você pode chamar UB se converter um valor que esteja fora do intervalo representável de uma enum para o tipo de enum. Isso ainda não se aplica ao cenário da pergunta, pois data[0]
é do tipo subjacente da enumeração (veja acima).
Observe que o CWG 1766 é considerado um defeito no Padrão; portanto, é aceito que os implementadores de compiladores se apliquem aos seus modos de compilação C ++ 11 e C ++ 14.
(*) char
deve ter pelo menos 8 bits de largura, mas não é necessário unsigned
. O valor máximo armazenável deve ser pelo menos 127
conforme o Anexo E da Norma C99.
Compare com [expr] / 4
Se durante a avaliação de uma expressão, o resultado não for matematicamente definido ou não estiver no intervalo de valores representáveis para seu tipo, o comportamento será indefinido.
Antes do CWG 1766, o tipo de conversão integral -> tipo de enumeração pode produzir um valor não especificado . A pergunta é: um valor não especificado pode estar fora dos valores representáveis para seu tipo? Eu acredito que a resposta é não - se a resposta for afirmativa , não haveria diferença nas garantias que você obtém para operações em tipos assinados entre "esta operação produz um valor não especificado" e "esta operação tem um comportamento indefinido".
Por isso, antes de CWG 1766, ainda static_cast<Color>(10000)
seria não invocar UB; mas depois de CWG 1766, que faz invocar UB.
Agora, a switch
declaração:
[stmt.switch] / 2
A condição deve ser do tipo integral, tipo de enumeração ou tipo de classe. [...] promoções integrais são realizadas.
[conv.prom] / 4
Um pré-valor de um tipo de enumeração sem escopo cujo tipo subjacente é fixo (7.2) pode ser convertido em um pré-valor de seu tipo subjacente. Além disso, se a promoção integral puder ser aplicada ao seu tipo subjacente, um pré-valor de um tipo de enumeração sem escopo cujo tipo subjacente é fixo também poderá ser convertido em um pré-valor do tipo subjacente promovido.
Nota: O tipo subjacente de uma enum com escopo sem enum-base é int
. Para enumerações sem escopo, o tipo subjacente é definido pela implementação, mas não deve ser maior que int
se int
puder conter os valores de todos os enumeradores.
Para uma enumeração sem escopo , isso nos leva a / 1
Um prvalue de um tipo inteiro diferente de bool
, char16_t
, char32_t
, ou wchar_t
cujo número inteiro conversão posto (4,13) é menor do que o grau de int
pode ser convertido para um prvalue de tipo int
se int
podem representar todos os valores do tipo de fonte; caso contrário, o pré-valor de origem pode ser convertido em um pré-valor do tipo unsigned int
.
No caso de uma enumeração sem escopo , estaríamos lidando com int
s aqui. Para enumerações com escopo definido ( enum class
e enum struct
), nenhuma promoção integral se aplica. De qualquer forma, a promoção integral também não leva ao UB, pois o valor armazenado está no intervalo do tipo subjacente e no intervalo de int
.
[stmt.switch] / 5
Quando a switch
instrução é executada, sua condição é avaliada e comparada com cada constante de caso. Se uma das constantes de caso for igual ao valor da condição, o controle será passado para a instrução após o case
rótulo correspondente . Se nenhuma case
constante corresponder à condição e se houver um default
rótulo, o controle passará para a instrução rotulada pelo default
rótulo.
o default
rótulo deve ser atingido.
Nota: Pode-se examinar novamente o operador de comparação, mas ele não é explicitamente usado na "comparação" referida. De fato, não há indícios de que isso introduza o UB para enumerações com ou sem escopo no nosso caso.
Como bônus, o padrão oferece garantias quanto a isso, mas com enumeração simples?
Se o enum
escopo é ou não não faz diferença aqui. No entanto, faz diferença se o tipo subjacente é fixo ou não. O [decl.enum] / 7 completo é:
Para uma enumeração cujo tipo subjacente é fixo, os valores da enumeração são os valores do tipo subjacente. Caso contrário, uma enumeração onde E min é o menor entrevistador e E max é o maior, os valores de enumeração são os valores na gama b min para b no máximo , definido do seguinte modo: Vamos K
ser 1
para a representação de um de complemento de dois e 0
por um complemento ou representação em magnitude de sinal. b max é o menor valor maior que ou igual a max (| e min | - K
, | e max |) e igual a 2M - 1 , ondeM
é um número inteiro não negativo. b min é zero se e min não for negativo e - (b max + K
), caso contrário.
Vamos dar uma olhada na seguinte enumeração:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Observe que não podemos definir isso como uma enumeração com escopo, pois todas as enumerações com escopo fixaram tipos subjacentes.
Felizmente, ColorUnfixed
o menor enumerador é red = 0x1
, então max (| e min | - K
, | e max |) é igual a | e max | em qualquer caso, o que é yellow = 0x2
. O menor valor maior ou igual a 2
, que é igual a 2 M - 1 para um número inteiro positivo, M
é 3
( 2 2 - 1 ). (Acho que a intenção é permitir que o alcance seja estendido em etapas de 1 bit.) Segue que b max é 3
e bmin é 0
.
Portanto, 100
estaria fora do intervalo de ColorUnfixed
e static_cast
produziria um valor não especificado antes do CWG 1766 e um comportamento indefinido após o CWG 1766.
char
, portanto "O valor é inalterado se o valor original estiver dentro do intervalo dos valores de enumeração (7.2)." aplica-se.