Czy wyliczenia C ++ są podpisane czy niepodpisane? A co za tym idzie, czy można bezpiecznie sprawdzić poprawność danych wejściowych, sprawdzając, czy jest to <= twoja maksymalna wartość, i pominąć> = twoją wartość minimalną (zakładając, że zacząłeś od 0 i zwiększałeś o 1)?
107
Odpowiedzi:
Nie powinieneś polegać na żadnej konkretnej reprezentacji. Przeczytaj poniższy link . Ponadto standard mówi, że jest zdefiniowane w implementacji, który typ całkowity jest używany jako typ bazowy dla wyliczenia, z wyjątkiem tego, że nie powinien być większy niż int, chyba że pewna wartość nie może pasować do int lub unsigned int.
W skrócie: nie można polegać na tym, że wyliczenie jest podpisane lub niepodpisane.
źródło
Przejdźmy do źródła. Oto, co mówi dokument normy C ++ 03 (ISO / IEC 14882: 2003) w 7.2-5 (Deklaracje wyliczenia):
Krótko mówiąc, twój kompilator może dokonać wyboru (oczywiście, jeśli masz liczby ujemne dla niektórych wartości wyliczenia, zostanie to podpisane).
źródło
Nie powinieneś polegać na tym, że są podpisane lub niepodpisane. Jeśli chcesz, aby były jawnie podpisane lub niepodpisane, możesz użyć następujących opcji:
źródło
Nie powinieneś polegać na tym, że jest podpisany lub niepodpisany. Zgodnie ze standardem jest zdefiniowane w implementacji, który typ całkowity jest używany jako typ bazowy dla wyliczenia. Jednak w większości implementacji jest to liczba całkowita ze znakiem.
W C ++ 0x zostaną dodane silnie wpisane wyliczenia , które pozwolą określić typ wyliczenia, na przykład:
Nawet teraz można jednak przeprowadzić prostą walidację, używając wyliczenia jako zmiennej lub typu parametru, takiego jak ten:
źródło
Kompilator może zdecydować, czy wyliczenia są podpisane czy niepodpisane.
Inną metodą sprawdzania poprawności wyliczeń jest użycie samego wyliczenia jako typu zmiennej. Na przykład:
źródło
Nawet niektóre stare odpowiedzi otrzymały 44 głosy za, nie zgadzam się ze wszystkimi z nich. Krótko mówiąc, nie sądzę, że powinniśmy przejmować
underlying type
się wyliczeniem.Po pierwsze, typ Enum w C ++ 03 jest odrębnym typem, który nie ma pojęcia o znaku. Ponieważ od standardu C ++ 03
dcl.enum
Więc kiedy mówimy o znaku typu wyliczenia, powiedzmy, porównując 2 operandy wyliczenia przy użyciu
<
operatora, w rzeczywistości mówimy o niejawnej konwersji typu wyliczenia na typ całkowity. Liczy się znak tego integralnego typu . A podczas konwersji wyliczenia na typ całkowity ma zastosowanie ta instrukcja:I najwyraźniej podstawowy typ wyliczenia nie ma nic wspólnego z promocją integralną. Ponieważ standard definiuje integralną promocję w następujący sposób:
W związku z tym, czy typ wyliczeniowy staje się
signed int
lubunsigned int
zależy od tego, czysigned int
może zawierać wszystkie wartości zdefiniowanych modułów wyliczających, a nie podstawowy typ wyliczenia.Zobacz moje pokrewne pytanie Znak typu wyliczenia w C ++ nieprawidłowy po przekonwertowaniu na typ całkowy
źródło
-Wsign-conversion
. Używamy go, aby pomóc wyłapać niezamierzone błędy w naszym kodzie. Ale +1 za cytowanie standardu i wskazanie, że wyliczenie nie ma żadnego typu (wsigned
porównaniuunsigned
) z nim.W przyszłości w C ++ 0x będą dostępne wyliczenia o jednoznacznie określonym typie i będą miały kilka zalet (takich jak bezpieczeństwo typów, jawne typy bazowe lub jawne określanie zakresu). Dzięki temu możesz być pewniejszy co do oznaczenia typu.
źródło
Oprócz tego, co inni powiedzieli o podpisaniu / niepodpisaniu, oto, co mówi norma o zakresie wyliczanego typu:
7.2 (6): „W przypadku wyliczenia, w którym e (min) jest najmniejszym modułem wyliczającym, a e (max) jest największym, wartości wyliczenia są wartościami typu bazowego w zakresie od b (min) do b (max ), gdzie b (min) i b (max) to odpowiednio najmniejsze i największe wartości najmniejszego pola bitowego, które może przechowywać e (min) i e (max). Możliwe jest zdefiniowanie wyliczenia, które ma niezdefiniowane wartości przez któregokolwiek z jego wyliczających. "
Na przykład:
definiuje typ wyliczeniowy, gdzie e (min) to 1, a e (max) to 4. Jeśli typ bazowy jest podpisany int, to najmniejsze wymagane pole bitowe ma 4 bity, a jeśli ints w twojej implementacji są uzupełnieniami do dwóch, to prawidłowy zakres wyliczenie to od -8 do 7. Jeśli typ bazowy jest bez znaku, to ma 3 bity, a zakres wynosi od 0 do 7. Sprawdź dokumentację kompilatora, jeśli Ci zależy (na przykład jeśli chcesz rzutować wartości całkowite inne niż moduły wyliczające na typ wyliczeniowy, to musisz wiedzieć, czy wartość jest w zakresie wyliczenia, czy nie - jeśli nie, wynikowa wartość wyliczenia jest nieokreślona).
To, czy te wartości są prawidłowymi danymi wejściowymi dla funkcji, może być inną kwestią niż to, czy są one prawidłowymi wartościami typu wyliczeniowego. Twój kod sprawdzający prawdopodobnie martwi się raczej o to pierwsze niż o drugie, więc w tym przykładzie powinno przynajmniej sprawdzać> = A i <= B.
źródło
Sprawdź to z
std::is_signed<std::underlying_type
wyliczeniami + zakres domyślnieint
https://en.cppreference.com/w/cpp/language/enum oznacza:
main.cpp
GitHub upstream .
Skompiluj i uruchom:
Wynik:
Testowano na Ubuntu 16.04, GCC 6.4.0.
źródło