Wyliczenie vs Wyliczenie silnie wpisane

84

Jestem początkującym w programowaniu w C ++.

Dziś trafiłem na nowy temat: mocno wpisany enum. Trochę to zbadałem, ale do tej pory nie jestem w stanie dowiedzieć się, dlaczego tego potrzebujemy i jaki jest pożytek z tego samego?

Na przykład, jeśli mamy:

enum xyz{a, b, c};
/*a = 0, b = 1, c = 2, (Typical C format)*/

Dlaczego musimy pisać:

enum class xyz{a, b, c};

Co my tu próbujemy zrobić? Moja najważniejsza wątpliwość dotyczy tego, jak go używać. Czy mógłbyś podać mały przykład, który pozwoli mi zrozumieć.

Rasmi Ranjan Nayak
źródło

Odpowiedzi:

114

OK, pierwszy przykład: wyliczenia w starym stylu nie mają własnego zakresu:

enum Animals {Bear, Cat, Chicken};
enum Birds {Eagle, Duck, Chicken}; // error! Chicken has already been declared!

enum class Fruits { Apple, Pear, Orange };
enum class Colours { Blue, White, Orange }; // no problem!

Po drugie, niejawnie konwertują się na typy całkowite, co może prowadzić do dziwnego zachowania:

bool b = Bear && Duck; // what?

Na koniec możesz określić podstawowy typ całkowity wyliczeń C ++ 11:

enum class Foo : char { A, B, C};

Wcześniej nie określono podstawowego typu, co mogło powodować problemy ze zgodnością między platformami. Edycja W komentarzach wskazano, że można również określić podstawowy typ całkowity wyliczenia „starego stylu” w C ++ 11.

juanchopanza
źródło
Czy musimy zadeklarować / zdefiniować enum class Coloursi enum class Fruits. Bo kiedy pisałem kod w VS 2010. zgłasza błąd "expects a defination or a tag name"mocy class.
Rasmi Ranjan Nayak,
Ponadto: dla „zwykłego” wyliczenia w C ++ 11, tak jak w C ++ 98, domyślny typ bazowy nie jest zdefiniowany
bruziuz
2
Ponadto: można wykonać deklarację do przodu wyliczeń, jeśli określono typ podstawowy. W przypadku zwykłego wyliczenia w C ++ 11 i C ++ 98 jest to niedozwolone. Kompilator Microsoftu pozwala na wykonanie deklaracji do przodu enum, ale jest to tylko rozszerzenie MS, nie jest standardem (na przykład gcc nie pozwala na to) Więc teraz jest to legalne: enum ForwardDeclare: std :: uint8_t;
bruziuz
Czy możemy mieć wyliczenia o określonym zakresie, które również niejawnie konwertują na typ całkowity?
SS Anne
17

Na tej stronie IBM jest dobry artykuł o wyliczeniach , jest bardzo szczegółowy i dobrze napisany. Oto kilka ważnych punktów w pigułce:

Wyliczenia o określonym zakresie rozwiązują większość ograniczeń występujących w zwykłych wyliczeniach: pełne bezpieczeństwo typu, dobrze zdefiniowany typ podstawowy, problemy z zakresem i deklaracja do przodu.

  • Bezpieczeństwo typów uzyskuje się, uniemożliwiając wszystkie niejawne konwersje wyliczeń o określonym zakresie na inne typy.
  • Otrzymujesz nowy zakres, a wyliczenie nie znajduje się już w zakresie obejmującym, co pozwala uniknąć konfliktów nazw.
  • Wyliczenia w zakresie daje możliwość określenia bazowego typu wyliczenia, aw przypadku wyliczeń o określonym zakresie wartość domyślna to int, jeśli zdecydujesz się go nie określać.
  • Dowolne wyliczenie ze stałym typem bazowym można zadeklarować do przodu.
SingerOfTheFall
źródło
2
Trzeci i czwarty punkt nie są specyficzne dla wyliczeń o określonym zakresie; możesz określić podstawowy typ dowolnego wyliczenia.
Mike Seymour
1
Czy ktoś ma link do mniej uszkodzonej wersji pliku PDF? Zawarte w nim przykłady kodu nie są renderowane w żadnej z moich przeglądarek PDF, co pozostawia wiele wyobraźni.
Sara Sinback
11

Wartości enum classsą naprawdę typu enum class, a nie underlying_typedla wyliczeń C.

enum xyz { a, b, c};
enum class xyz_c { d, f, e };

void f(xyz x)
{
}

void f_c(xyz_c x)
{
}

// OK.
f(0);
// OK for C++03 and C++11.
f(a);
// OK with C++11.
f(xyz::a);
// ERROR.
f_c(0);
// OK.
f_c(xyz_c::d);
Na zawsze
źródło
5

Klasy wyliczeniowe („nowe wyliczenia”, „silne wyliczenia”) rozwiązują trzy problemy z tradycyjnymi wyliczeniami w języku C ++:

  1. konwencjonalne enumsniejawnie konwertuj naint , powodując błędy, gdy ktoś nie chce, aby wyliczenie działało jak liczba całkowita.
  2. standardowy enums eksportują swoje moduły wyliczające do otaczającego zakresu, powodując konflikty nazw.
  3. enumNie można określić bazowego typu an , co powoduje zamieszanie, problemy ze zgodnością i uniemożliwia przekazanie deklaracji.

enum class („silne wyliczenia”) są silnie wpisane i mają określony zakres:

enum Alert { green, yellow, orange, red }; // traditional enum

enum class Color { red, blue };   // scoped and strongly typed enum
                                  // no export of enumerator names into enclosing scope
                                  // no implicit conversion to int
enum class TrafficLight { red, yellow, green };

Alert a = 7;              // error (as ever in C++)
Color c = 7;              // error: no int->Color conversion

int a2 = red;             // ok: Alert->int conversion
int a3 = Alert::red;      // error in C++98; ok in C++11
int a4 = blue;            // error: blue not in scope
int a5 = Color::blue;     // error: not Color->int conversion

Color a6 = Color::blue;   // ok

Jak pokazano, tradycyjne wyliczenia działają jak zwykle, ale można teraz opcjonalnie kwalifikować się przy użyciu nazwy wyliczenia.

Nowe wyliczenia są „klasami wyliczeń”, ponieważ łączą aspekty tradycyjnych wyliczeń (wartości nazw) z aspektami klas (elementy członkowskie o określonym zakresie i brak konwersji).

Możliwość określenia podstawowego typu pozwala na prostszą interoperacyjność i gwarantowane rozmiary wyliczeń:

enum class Color : char { red, blue };  // compact representation

enum class TrafficLight { red, yellow, green };  // by default, the underlying type is int

enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U };   // how big is an E?
                                                 // (whatever the old rules say;
                                                 // i.e. "implementation defined")

enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U };   // now we can be specific

Umożliwia również przekazywanie do przodu wyliczeń:

enum class Color_code : char;     // (forward) declaration
void foobar(Color_code* p);       // use of forward declaration
// ...
enum class Color_code : char { red, yellow, green, blue }; // definition

Typ bazowy musi być jednym z typów całkowitych ze znakiem lub bez znaku; wartość domyślna to int.

W bibliotece standardowej enumklasy służą do:

  1. Systemy odwzorowania specyficzne kody błędów: W <system_error>: enum class errc;
  2. Wskaźniki bezpieczeństwa wskazówki: W <memory>:enum class pointer_safety { relaxed, preferred, strict };
  3. Błędy strumienia we / wy: w <iosfwd>:enum class io_errc { stream = 1 };
  4. Asynchroniczna obsługa błędów komunikacji: W <future>:enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Kilka z nich ma operatory, takie jak ==zdefiniowano.

Jnana
źródło
3

Enum Scope

Wyliczenia eksportują swoje moduły wyliczające do otaczającego zakresu. Ma to dwie wady. Po pierwsze, może prowadzić do konfliktów nazw, jeśli dwa wyliczacze w różnych wyliczeniach zadeklarowanych w tym samym zakresie mają tę samą nazwę; po drugie, nie jest możliwe użycie modułu wyliczającego z w pełni kwalifikowaną nazwą, w tym nazwą wyliczenia.

enum ESet {a0, a, a1, b1, c3};
enum EAlpha{a, b, c}

select = ESet::a; // error
select = a;       // is ambigious
Baran
źródło