Enum vs enumeración fuertemente tipada

84

Soy un principiante en programación C ++.

Hoy me encuentro con un nuevo tema: fuertemente tipado enum. Lo he investigado un poco, pero hasta ahora no he podido averiguar por qué lo necesitamos y para qué sirve.

Por ejemplo si tenemos:

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

Por qué necesitamos escribir:

enum class xyz{a, b, c};

¿Qué estamos tratando de hacer aquí? Mi duda más importante es cómo usarlo. ¿Podría dar un pequeño ejemplo que me haga entender?

Rasmi Ranjan Nayak
fuente

Respuestas:

114

Bien, primer ejemplo: las enumeraciones de estilo antiguo no tienen su propio alcance:

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!

En segundo lugar, se convierten implícitamente en tipos integrales, lo que puede provocar un comportamiento extraño:

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

Finalmente, puede especificar el tipo integral subyacente de enumeraciones de C ++ 11:

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

Anteriormente, no se especificaba el tipo subyacente, lo que podría causar problemas de compatibilidad entre plataformas. Editar Se ha señalado en los comentarios que también puede especificar el tipo integral subyacente de una enumeración de "estilo antiguo" en C ++ 11.

juanchopanza
fuente
¿Necesitamos declarar / definir enum class Coloursy enum class Fruits. Porque cuando escribí el código en VS 2010. arroja un error "expects a defination or a tag name"debajo class.
Rasmi Ranjan Nayak
Además: para la enumeración "ordinaria" en C ++ 11 como en C ++ 98, el tipo subyacente predeterminado no está definido
bruziuz
2
Además: puede hacer una declaración directa de enum-s, si se especifica el tipo subyacente. Para la enumeración ordinaria en C ++ 11, C ++ 98 no está permitido. El compilador de Microsoft le permite hacer la declaración de reenvío de enum, pero es solo una extensión de MS, no es estándar (por ejemplo, gcc no lo permite). Así que eso ahora es legal: enum ForwardDeclare: std :: uint8_t;
bruziuz
¿Podemos tener enumeraciones de ámbito que también se conviertan implícitamente en un tipo integral?
SS Anne
17

Hay un buen artículo sobre enumeraciones en esta página de IBM , es muy detallado y está bien escrito. Aquí hay algunos puntos importantes en pocas palabras:

Las enumeraciones con ámbito resuelven la mayoría de las limitaciones en las que incurren las enumeraciones regulares: seguridad de tipo completa, tipo subyacente bien definido, problemas de alcance y declaración de reenvío.

  • Obtiene seguridad de tipos al no permitir todas las conversiones implícitas de enumeraciones con ámbito a otros tipos.
  • Obtiene un nuevo alcance, y la enumeración ya no está en el alcance adjunto, salvándose de conflictos de nombres.
  • Las enumeraciones con ámbito le ofrecen la posibilidad de especificar el tipo subyacente de la enumeración y, para las enumeraciones con ámbito, el valor predeterminado es int si elige no especificarlo.
  • Cualquier enumeración con un tipo subyacente fijo se puede declarar hacia adelante.
SingerOfTheFall
fuente
2
Los puntos tercero y cuarto no son específicos de las enumeraciones con alcance; puede especificar el tipo subyacente de cualquier enumeración.
Mike Seymour
1
¿Alguien tiene un enlace a una versión menos rota del PDF? Los ejemplos de código que contiene no se muestran en ninguno de mis visores de PDF, lo que deja mucho a la imaginación.
Sara Sinback
11

Los valores de enum classson realmente de tipo enum class, no underlying_typecomo para C-enumeraciones.

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);
Siempre
fuente
5

Las clases de enumeración ("enumeraciones nuevas", "enumeraciones fuertes") abordan tres problemas con las enumeraciones tradicionales de C ++:

  1. convencional enumsimplícitamente se convierte en int, provocando errores cuando alguien no quiere que una enumeración actúe como un número entero.
  2. enumsexportan sus enumeradores convencionales al ámbito circundante, lo que provoca conflictos de nombres.
  3. El tipo subyacente de an enumno se puede especificar, lo que genera confusión, problemas de compatibilidad y hace imposible la declaración hacia adelante.

enum class ("enumeraciones fuertes") están fuertemente tipados y con alcance:

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

Como se muestra, las enumeraciones tradicionales funcionan como de costumbre, pero ahora puede calificar opcionalmente con el nombre de la enumeración.

Las nuevas enumeraciones son "clase de enumeración" porque combinan aspectos de enumeraciones tradicionales (valores de nombres) con aspectos de clases (miembros de ámbito y ausencia de conversiones).

Ser capaz de especificar el tipo subyacente permite una interoperabilidad más simple y tamaños garantizados de enumeraciones:

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

También permite la declaración hacia adelante de enumeraciones:

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

El tipo subyacente debe ser uno de los tipos enteros con o sin signo; el predeterminado es int.

En la biblioteca estándar, las enumclases se utilizan para:

  1. Sistemas de cartografía códigos de error específicos: En <system_error>: enum class errc;
  2. Indicadores de seguridad de puntero: En <memory>:enum class pointer_safety { relaxed, preferred, strict };
  3. Errores de flujo de E / S: En <iosfwd>:enum class io_errc { stream = 1 };
  4. Manejo de errores de comunicaciones asincrónicas: En <future>:enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Varios de estos tienen operadores, como los ==definidos.

Jnana
fuente
3

Alcance de la enumeración

Las enumeraciones exportan sus enumeradores al ámbito circundante. Esto tiene dos inconvenientes. Primero, puede dar lugar a conflictos de nombres, si dos enumeradores en diferentes enumeraciones declaradas en el mismo ámbito tienen el mismo nombre; en segundo lugar, no es posible utilizar un enumerador con un nombre completo, incluido el nombre de enumeración.

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

select = ESet::a; // error
select = a;       // is ambigious
RAM
fuente