Czym jest ustawiony kolor zgodnie ze standardem?
Odpowiadając cytatem ze standardów C ++ 11 i C ++ 14:
[expr.static.cast] / 10
Wartość typu całkowitego lub wyliczeniowego można jawnie przekonwertować na typ wyliczeniowy. Wartość pozostaje niezmieniona, jeśli oryginalna wartość mieści się w zakresie wartości wyliczenia (7.2). W przeciwnym razie wynikowa wartość jest nieokreślona (i może nie znajdować się w tym zakresie).
Spójrzmy na zakres wartości wyliczenia : [dcl.enum] / 7
W przypadku wyliczenia, którego typ bazowy jest ustalony, wartości wyliczenia są wartościami typu podstawowego.
Przed CWG 1766 (C ++ 11, C ++ 14) W
związku z tym dla for data[0] == 100
, wynikowa wartość jest określona (*) i nie jest zaangażowane żadne niezdefiniowane zachowanie (UB) . Mówiąc bardziej ogólnie, podczas rzutowania z typu podstawowego na typ wyliczeniowy żadna wartość w nie data[0]
może prowadzić do UB dla static_cast
.
Po CWG 1766 (C ++ 17)
Zobacz defekt CWG 1766 . Przycisk [expr.static.cast] p10 ustęp została wzmocniona, więc teraz może powołać UB jeśli rzucisz wartość, która wykracza poza zakres reprezentowalna wyliczenia do typu enum. To nadal nie dotyczy scenariusza w pytaniu, ponieważ data[0]
jest to podstawowy typ wyliczenia (patrz wyżej).
Należy pamiętać, że CWG 1766 jest uważane za wadę w standardzie, dlatego też jest akceptowane, aby implementatorzy kompilatora stosowali się do swoich trybów kompilacji C ++ 11 i C ++ 14.
(*) char
musi mieć co najmniej 8 bitów szerokości, ale nie musi unsigned
. Wymagana jest maksymalna wartość, jaką można zapisać, co najmniej 127
zgodnie z załącznikiem E normy C99.
Porównaj z [wyraż] / 4
Jeśli podczas oceny wyrażenia wynik nie jest matematycznie zdefiniowany lub nie mieści się w zakresie reprezentowalnych wartości dla jego typu, zachowanie jest niezdefiniowane.
Przed CWG 1766 typ całkowity konwersji -> typ wyliczeniowy może generować nieokreśloną wartość . Pytanie brzmi: czy nieokreślona wartość może znajdować się poza reprezentowalnymi wartościami dla swojego typu? Uważam, że odpowiedź brzmi nie - jeśli odpowiedź byłaby twierdząca , nie byłoby żadnej różnicy w gwarancjach, jakie otrzymujesz dla operacji na typach ze znakiem między „ta operacja daje nieokreśloną wartość” i „ta operacja ma nieokreślone zachowanie”.
Stąd przed działań grupy roboczej 1766, nawet static_cast<Color>(10000)
by nie invoke UB; ale po działań grupy roboczej 1766, to robi invoke UB.
Teraz switch
oświadczenie:
[stmt.switch] / 2
Warunek powinien być typu całkowitego, typu wyliczenia lub typu klasy. [...] Prowadzone są promocje integralne.
[konw.prom] / 4
Wartość prvalue typu wyliczenia bez zakresu, którego typ bazowy jest ustalony (7.2), można przekonwertować na wartość prvalue jego typu bazowego. Ponadto, jeśli integralną promocję można zastosować do jej typu podstawowego, prvalue typu wyliczenia bez zakresu, którego typ bazowy jest ustalony, można również przekonwertować na prvalue promowanego typu podstawowego.
Uwaga: bazowy typ wyliczenia o określonym zakresie bez podstawy wyliczenia to int
. W przypadku wyliczeń bez zakresu typ podstawowy jest zdefiniowany w ramach implementacji, ale nie powinien być większy niż int
jeśli int
może zawierać wartości wszystkich modułów wyliczających.
W przypadku wyliczenia bez zakresu prowadzi to do / 1
Prvalue od typu całkowitych innych niż bool
, char16_t
, char32_t
lub wchar_t
której całkowita konwersja stopień (4,13) jest mniejszy niż stopień int
może być przekształcany do prvalue typu int
czy int
może reprezentować wszystkie wartości typu źródłowego; w przeciwnym razie źródłowa prvalue może zostać przekonwertowana na prvalue typu unsigned int
.
W przypadku unscoped wyliczenie, będziemy mieć do czynienia ze int
s tutaj. W przypadku wyliczeń w określonym zakresie ( enum class
i enum struct
) nie ma zastosowania żadna promocja integralna. W żaden sposób integralna promocja również nie prowadzi do UB, ponieważ przechowywana wartość znajduje się w zakresie typu bazowego iw zakresie int
.
[stmt.switch] / 5
Kiedy switch
instrukcja jest wykonywana, jej stan jest oceniany i porównywany z każdą stałą przypadku. Jeśli jedna ze stałych wielkości przypadku jest równa wartości warunku, sterowanie jest przekazywane do instrukcji następującej po dopasowanej case
etykiecie. Jeśli żadna case
stała nie pasuje do warunku i jeśli istnieje default
etykieta, sterowanie przechodzi do instrukcji oznaczonej default
etykietą.
default
Etykieta powinna być hit.
Uwaga: można spojrzeć jeszcze raz na operator porównania, ale nie jest on wyraźnie używany w opisywanym „porównaniu”. W rzeczywistości nie ma żadnej wskazówki, że w naszym przypadku wprowadziłoby to UB dla wyliczeń z zakresem lub bez zakresu.
Jako bonus, czy standard zapewnia jakiekolwiek gwarancje, ale z prostym wyliczeniem?
To, czy enum
jest to zakres, nie ma tutaj znaczenia. Jednak ma znaczenie, czy typ bazowy jest ustalony, czy nie. Kompletne [decl.enum] / 7 to:
W przypadku wyliczenia, którego typ bazowy jest ustalony, wartości wyliczenia są wartościami typu podstawowego. W przeciwnym razie, dla wyliczenia, w którym e min jest najmniejszym wyliczaczem, a e max jest największym, wartościami wyliczenia są wartości z zakresu od b min do b max , zdefiniowane w następujący sposób: Niech K
będzie 1
dla reprezentacji dopełnienia do dwóch i 0
dla a czyjś dopełnienie lub reprezentacja wielkości znaku. b max jest najmniejszą wartością większą lub równą max (| e min | - K
, | e max |) i równą 2M - 1 , gdzieM
jest nieujemną liczbą całkowitą. b min wynosi zero, jeśli e min jest nieujemne i - (b max + K
) w przeciwnym razie.
Rzućmy okiem na następujące wyliczenie:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Należy zauważyć, że nie możemy tego zdefiniować jako wyliczenia o określonym zakresie, ponieważ wszystkie wyliczenia w zakresie mają ustalone typy podstawowe.
Na szczęście ColorUnfixed
najmniejszy moduł wyliczający to red = 0x1
, więc max (| e min | - K
, | e max |) jest równe | e max | w każdym razie, czyli yellow = 0x2
. Najmniejsza wartość większa lub równa 2
, która jest równa 2 M - 1 dla dodatniej liczby całkowitej M
to 3
( 2 2 - 1 ). (Myślę, że celem jest zezwolenie na rozszerzenie zakresu w krokach 1-bitowych.) Wynika z tego, że b max jest, 3
a bmin jest 0
.
W związku z tym 100
byłoby poza zakresem ColorUnfixed
i static_cast
spowodowałoby nieokreśloną wartość przed CWG 1766 i niezdefiniowane zachowanie po CWG 1766.
char
, więc „Wartość pozostaje niezmieniona, jeśli oryginalna wartość mieści się w zakresie wartości wyliczenia (7.2)”. dotyczy.