Enum vs Enum fortemente tipizzato


84

Sono un principiante nella programmazione C ++.

Oggi mi imbatto in un nuovo argomento: fortemente digitato enum. Ho studiato un po ', ma fino ad ora non sono in grado di scoprire perché ne abbiamo bisogno ea che cosa serve?

Ad esempio se abbiamo:

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

Perché dobbiamo scrivere:

enum class xyz{a, b, c};

Cosa stiamo cercando di fare qui? Il mio dubbio più importante è come usarlo. Potresti fornire un piccolo esempio, che mi farà capire.

Risposte:


114

OK, primo esempio: le enumerazioni vecchio stile non hanno un proprio ambito:

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!

In secondo luogo, si convertono implicitamente in tipi integrali, il che può portare a comportamenti strani:

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

Infine, puoi specificare il tipo integrale sottostante delle enumerazioni C ++ 11:

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

In precedenza, il tipo sottostante non era specificato, il che poteva causare problemi di compatibilità tra le piattaforme. Modifica È stato sottolineato nei commenti che è anche possibile specificare il tipo integrale sottostante di un'enumerazione "vecchio stile" in C ++ 11.


Abbiamo bisogno di dichiarare / definire enum class Colourse enum class Fruits. Perché quando ho scritto il codice in VS 2010. Genera un errore "expects a defination or a tag name"sotto class.
Rasmi Ranjan Nayak,

Inoltre: per l'
enumerazione

2
Inoltre: è possibile eseguire dichiarazioni in avanti di enum-s, se il tipo sottostante specificato. Per l'enumerazione ordinaria in C ++ 11, C ++ 98 non è consentito. Il compilatore Microsoft ti consente di fare la dichiarazione in avanti di enum, ma è solo l'estensione MS, non è standard (ad esempio gcc non lo consente) Quindi questa cosa ora è legale: enum ForwardDeclare: std :: uint8_t;
bruziuz

Possiamo avere enumerazioni con ambito che si convertono anche implicitamente in un tipo integrale?
SS Anne

1
@SSAnne No, la conversione implicita vanifica piuttosto lo scopo di un enum fortemente tipizzato. Definire una funzione modello per eseguire invece esplicitamente la conversione; usa std :: sottostante_type <T> :: type per estrarre il tipo.
David R

17

C'è un buon articolo sulle enumerazioni in questa pagina IBM , è molto dettagliato e ben scritto. Ecco alcuni punti importanti in poche parole:

Le enumerazioni con ambito risolvono la maggior parte delle limitazioni sostenute dalle enumerazioni regolari: sicurezza dei tipi completa, tipo sottostante ben definito, problemi di ambito e dichiarazione anticipata.

  • Si ottiene l'indipendenza dai tipi disabilitando tutte le conversioni implicite di enumerazioni con ambito in altri tipi.
  • Si ottiene un nuovo ambito e l'enum non è più nello scope di inclusione, salvandosi dai conflitti di nome.
  • Le enumerazioni con ambito ti danno la possibilità di specificare il tipo sottostante dell'enumerazione e per le enumerazioni con ambito, il valore predefinito è int se scegli di non specificarlo.
  • Qualsiasi enum con un tipo sottostante fisso può essere dichiarato in avanti.

2
Il terzo e il quarto punto non sono specifici per le enumerazioni con ambito; è possibile specificare il tipo sottostante di qualsiasi enumerazione.
Mike Seymour

1
Qualcuno ha un collegamento a una versione meno danneggiata del PDF? Gli esempi di codice in esso contenuti non vengono visualizzati in nessuno dei miei visualizzatori di PDF, il che lascia molto all'immaginazione.
Sara Sinback

11

I valori di enum classè davvero di tipo enum class, non underlying_typecome per gli enumeratori 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);

5

Le classi enum ("new enums", "strong enums") risolvono tre problemi con le enumerazioni C ++ tradizionali:

  1. convenzionale enumsconverte implicitamente in int, causando errori quando qualcuno non vuole che un'enumerazione agisca come un numero intero.
  2. convenzionale enumsesportare i propri enumeratori nello scope circostante, provocando conflitti di nome.
  3. Il tipo sottostante di un enumnon può essere specificato, causando confusione, problemi di compatibilità e rendendo impossibile la dichiarazione anticipata.

enum class ("enumerazioni forti") sono fortemente tipizzati e con ambito:

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

Come mostrato, le enumerazioni tradizionali funzionano come al solito, ma ora puoi facoltativamente qualificarti con il nome dell'enumerazione.

Le nuove enumerazioni sono "classe enum" perché combinano aspetti delle enumerazioni tradizionali (valori dei nomi) con aspetti delle classi (membri con ambito e assenza di conversioni).

Essere in grado di specificare il tipo sottostante consente un'interoperabilità più semplice e dimensioni garantite delle enumerazioni:

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

Consente inoltre la dichiarazione anticipata di enumerazioni:

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

Il tipo sottostante deve essere uno dei tipi interi con segno o senza segno; l'impostazione predefinita è int.

Nella libreria standard, le enumclassi vengono utilizzate per:

  1. Codici specifici sistemi di mappatura di errore: In <system_error>: enum class errc;
  2. Indicatori di sicurezza del puntatore: In <memory>:enum class pointer_safety { relaxed, preferred, strict };
  3. Errori di flusso di I / O: In <iosfwd>:enum class io_errc { stream = 1 };
  4. Gestione degli errori di comunicazione asincrona: In <future>:enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Molti di questi hanno operatori, come ==definito.


3

Ambito di enumerazione

Le enumerazioni esportano i loro enumeratori nell'ambito circostante. Ciò presenta due inconvenienti. Primo, può portare a conflitti di nome, se due enumeratori in enumerazioni diverse dichiarate nello stesso ambito hanno lo stesso nome; secondo, non è possibile utilizzare un enumeratore con un nome completo, incluso il nome dell'enumerazione.

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

select = ESet::a; // error
select = a;       // is ambigious
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.