Enum vs enum fortemente tipado


84

Sou um iniciante em programação C ++.

Hoje me deparei com um novo tópico: fortemente tipado enum. Pesquisei um pouco, mas até agora não consigo descobrir por que precisamos disso e para que serve?

Por exemplo, se tivermos:

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

Por que precisamos escrever:

enum class xyz{a, b, c};

O que estamos tentando fazer aqui? Minha dúvida mais importante é como usá-lo. Você poderia dar um pequeno exemplo, que me faça entender.

Respostas:


114

OK, primeiro exemplo: enums de estilo antigo não têm escopo próprio:

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!

Em segundo lugar, eles se convertem implicitamente em tipos integrais, o que pode levar a um comportamento estranho:

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

Por fim, você pode especificar o tipo integral subjacente de enums C ++ 11:

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

Anteriormente, o tipo subjacente não era especificado, o que poderia causar problemas de compatibilidade entre as plataformas. Editar Foi apontado em comentários que você também pode especificar o tipo integral subjacente de um enum de "estilo antigo" no C ++ 11.


Precisamos declarar / definir enum class Colourse enum class Fruits. Porque quando escrevi o código no VS 2010. Ele lança um erro "expects a defination or a tag name"em class.
Rasmi Ranjan Nayak

Além disso: Para enum "comum" em C ++ 11 como em C ++ 98, o tipo subjacente padrão não está definido
bruziuz 01 de

2
Além disso: você pode fazer a declaração de encaminhamento de enum-s, se o tipo subjacente especificado. Para enum comum em C ++ 11, C ++ 98, não é permitido. O compilador da Microsoft permite que você faça a declaração de encaminhamento de enum, mas é apenas uma extensão da MS, não é padrão (por exemplo, o gcc não permite). Portanto, agora é legal: enum ForwardDeclare: std :: uint8_t;
bruziuz 01 de

Podemos ter enums com escopo que também se convertem implicitamente em um tipo integral?
SS Anne

17

Há um bom artigo sobre enums nesta página da IBM , é muito detalhado e bem escrito. Aqui estão alguns pontos importantes em poucas palavras:

Os enums com escopo resolvem a maioria das limitações incorridas por enums regulares: segurança de tipo completa, tipo subjacente bem definido, problemas de escopo e declaração de encaminhamento.

  • Você obtém segurança de tipo ao não permitir todas as conversões implícitas de enums com escopo definido para outros tipos.
  • Você obtém um novo escopo e o enum não está mais no escopo delimitador, salvando-se de conflitos de nome.
  • Os enums com escopo permitem especificar o tipo subjacente da enumeração e, para os enums com escopo, o padrão é int se você optar por não especificá-lo.
  • Qualquer enum com um tipo subjacente fixo pode ser declarado para frente.

2
O terceiro e o quarto pontos não são específicos para enumerações com escopo definido; você pode especificar o tipo subjacente de qualquer enumeração.
Mike Seymour

1
Alguém tem um link para uma versão menos corrompida do PDF? Os exemplos de código nele não são renderizados em nenhum dos meus visualizadores de PDF, o que deixa muito para a imaginação.
Sara Sinback

11

Os valores de enum classé realmente do tipo enum class, não underlying_typecomo para C-enums.

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

As classes enum ("novos enums", "enums fortes") abordam três problemas com enumerações C ++ tradicionais:

  1. convencional enumsimplicitamente converter em int, causando erros quando alguém não deseja que uma enumeração atue como um inteiro.
  2. convencional enumsexportar seus enumeradores para o escopo circundante, causando conflitos de nome.
  3. O tipo subjacente de an enumnão pode ser especificado, causando confusão, problemas de compatibilidade e impossibilitando a declaração de encaminhamento.

enum class ("enums fortes") são fortemente tipados e com escopo definido:

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

Conforme mostrado, enums tradicionais funcionam como de costume, mas agora você pode se qualificar opcionalmente com o nome do enum.

Os novos enums são "classe enum" porque combinam aspectos de enumerações tradicionais (valores de nomes) com aspectos de classes (membros com escopo definido e ausência de conversões).

Ser capaz de especificar o tipo subjacente permite uma interoperabilidade mais simples e tamanhos garantidos de enumerações:

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

Ele também permite a declaração de enums:

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

O tipo subjacente deve ser um dos tipos inteiros assinados ou não assinados; o padrão é int.

Na biblioteca padrão, as enumclasses são usadas para:

  1. Códigos de sistemas de mapeamento específicos do erro: Em <system_error>: enum class errc;
  2. Indicadores de segurança do ponteiro: Em <memory>:enum class pointer_safety { relaxed, preferred, strict };
  3. Erros de fluxo de E / S: Em <iosfwd>:enum class io_errc { stream = 1 };
  4. Tratamento de erros de comunicação assíncrona: Em <future>:enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Vários deles têm operadores, conforme ==definido.


3

Escopo Enum

As enumerações exportam seus enumeradores para o escopo circundante. Isso tem duas desvantagens. Em primeiro lugar, pode levar a conflitos de nome, se dois enumeradores em enums diferentes declarados no mesmo escopo tiverem o mesmo nome; segundo, não é possível usar um enumerador com um nome totalmente qualificado, incluindo o nome enum.

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

select = ESet::a; // error
select = a;       // is ambigious
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.