Qual è il colore impostato secondo lo standard?
Rispondere con una citazione dagli standard C ++ 11 e C ++ 14:
[Expr.static.cast] / 10
Un valore di tipo integrale o di enumerazione può essere esplicitamente convertito in un tipo di enumerazione. Il valore rimane invariato se il valore originale è compreso nell'intervallo dei valori di enumerazione (7.2). In caso contrario, il valore risultante non è specificato (e potrebbe non essere compreso in tale intervallo).
Esaminiamo l' intervallo dei valori di enumerazione : [dcl.enum] / 7
Per un'enumerazione il cui tipo sottostante è fisso, i valori dell'enumerazione sono i valori del tipo sottostante.
Prima di CWG 1766 (C ++ 11, C ++ 14)
Pertanto, per data[0] == 100
, viene specificato il valore risultante (*) e non è coinvolto nessun comportamento indefinito (UB) . Più in generale, quando si esegue il cast dal tipo sottostante al tipo di enumerazione, nessun valore in data[0]
può portare a UB per il file static_cast
.
Dopo CWG 1766 (C ++ 17)
Vedi difetto CWG 1766 . Il paragrafo [expr.static.cast] p10 è stato rafforzato, quindi ora puoi invocare UB se lanci un valore al di fuori dell'intervallo rappresentabile di un enum nel tipo enum. Questo non si applica ancora allo scenario nella domanda, poiché data[0]
è del tipo sottostante dell'enumerazione (vedi sopra).
Si noti che CWG 1766 è considerato un difetto dello standard, quindi è accettato per gli implementatori di compilatori applicare alle loro modalità di compilazione C ++ 11 e C ++ 14.
(*) char
deve essere largo almeno 8 bit, ma non lo è unsigned
. Il valore massimo memorizzabile deve essere almeno 127
conforme all'allegato E della norma C99.
Confronta con [expr] / 4
Se durante la valutazione di un'espressione, il risultato non è definito matematicamente o non è compreso nell'intervallo di valori rappresentabili per il suo tipo, il comportamento non è definito.
Prima di CWG 1766, il tipo integrale di conversione -> tipo di enumerazione può produrre un valore non specificato . La domanda è: un valore non specificato può essere esterno ai valori rappresentabili per il suo tipo? Credo che la risposta sia no : se la risposta fosse sì , non ci sarebbe alcuna differenza nelle garanzie che si ottengono per le operazioni sui tipi firmati tra "questa operazione produce un valore non specificato" e "questa operazione ha un comportamento indefinito".
Quindi, prima CWG 1766, anche static_cast<Color>(10000)
sarebbe non invoke UB; ma dopo CWG 1766, si fa invoke UB.
Ora, la switch
dichiarazione:
[Stmt.switch] / 2
La condizione deve essere di tipo integrale, tipo di enumerazione o tipo di classe. [...] Vengono eseguite promozioni integrali.
[Conv.prom] / 4
Una prvalue di un senza ambito tipo enumerazione i cui sottostante tipo è fissato (7.2) può essere convertito in un prvalue del suo tipo sottostante. Inoltre, se la promozione integrale può essere applicata al suo tipo sottostante, un valore di un tipo di enumerazione senza ambito il cui tipo sottostante è fisso può anche essere convertito in un valore del tipo sottostante promosso.
Nota: il tipo di base di un enum con ambito senza enum-base è int
. Per gli enum senza ambito il tipo sottostante è definito dall'implementazione, ma non deve essere più grande di int
se int
può contenere i valori di tutti gli enumeratori.
Per un'enumerazione senza ambito , questo ci porta a / 1
Una prvalue di un tipo intero diverso bool
, char16_t
, char32_t
, o wchar_t
il cui intero conversione rango (4.13) è inferiore alla posizione di int
può essere convertito in un prvalue di tipo int
se int
può rappresentare tutti i valori del tipo di sorgente; in caso contrario, il valore di origine può essere convertito in un valore di tipo unsigned int
.
Nel caso di un senza ambito enumerazione, avremmo a che fare con int
s qui. Per le enumerazioni con ambito ( enum class
e enum struct
), non si applica alcuna promozione integrale. In ogni caso, la promozione integrale non porta nemmeno a UB, poiché il valore memorizzato è compreso nell'intervallo del tipo sottostante e nell'intervallo di int
.
[Stmt.switch] / 5
Quando switch
viene eseguita l' istruzione, la sua condizione viene valutata e confrontata con ogni costante di caso. Se una delle costanti del caso è uguale al valore della condizione, il controllo viene passato all'istruzione che segue l' case
etichetta corrispondente . Se nessuna case
costante corrisponde alla condizione e se esiste default
un'etichetta, il controllo passa all'istruzione etichettata default
dall'etichetta.
L' default
etichetta dovrebbe essere colpita.
Nota: si potrebbe dare un'altra occhiata all'operatore di confronto, ma non è esplicitamente usato nel "confronto" riferito. In realtà, non c'è alcun suggerimento che introdurrebbe UB per enumerazioni con ambito o senza ambito nel nostro caso.
Come bonus, lo standard offre garanzie a riguardo ma con enum chiaro?
Se l' enum
ambito è o meno non fa alcuna differenza qui. Tuttavia, fa la differenza se il tipo sottostante è riparato o meno. Il [decl.enum] completo / 7 è:
Per un'enumerazione il cui tipo sottostante è fisso, i valori dell'enumerazione sono i valori del tipo sottostante. In caso contrario, per un'enumerazione dove e min è il più piccolo enumeratore ed e max è la più grande, i valori della enumerazione sono i valori nel range b min a b massimo , definito come segue: Let K
sia 1
per la rappresentazione di un complemento a due e 0
per un complemento o rappresentazione della grandezza del segno. b max è il valore più piccolo maggiore o uguale a max (| e min | - K
, | e max |) e uguale a 2M - 1 , doveM
è un numero intero non negativo. b min è zero se e min non è negativo e - (b max + K
) in caso contrario.
Diamo un'occhiata alla seguente enumerazione:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Si noti che non è possibile definirlo come enum con ambito, poiché tutti gli enum con ambito hanno tipi fissi sottostanti.
Fortunatamente, ColorUnfixed
l'enumeratore più piccolo è red = 0x1
, quindi max (| e min | - K
, | e max |) è uguale a | e max | in ogni caso, che è yellow = 0x2
. Il valore più piccolo maggiore o uguale a 2
, che è uguale a 2 M - 1 per un numero intero positivo M
è 3
( 2 2 - 1 ). (Penso che l'intenzione sia quella di consentire l'estensione dell'intervallo in incrementi di 1 bit.) Ne consegue che b max è 3
e bmin è 0
.
Pertanto, 100
sarebbe al di fuori dell'intervallo di ColorUnfixed
e static_cast
produrrebbe un valore non specificato prima di CWG 1766 e un comportamento indefinito dopo CWG 1766.
char
, quindi "Il valore rimane invariato se il valore originale è compreso nell'intervallo dei valori di enumerazione (7.2)." si applica.