web-dev-qa-db-fra.com

Enum vs Fortement typé enum

Je suis un débutant en programmation C++.

Aujourd'hui, je tombe sur un nouveau sujet: fortement typé enum. Je l'ai un peu étudié, mais jusqu'à présent, je suis incapable de savoir pourquoi avons-nous besoin de cela et à quoi sert-il?

Par exemple si nous avons:

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

Pourquoi avons-nous besoin d'écrire:

enum class xyz{a, b, c};

Qu'essayons-nous de faire ici? Mon doute le plus important est de savoir comment l'utiliser. Pourriez-vous donner un petit exemple, qui me fera comprendre.

78
Rasmi Ranjan Nayak

OK, premier exemple: les enums de style ancien n'ont pas leur propre portée:

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!

Deuxièmement, ils se convertissent implicitement en types intégraux, ce qui peut entraîner un comportement étrange:

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

Enfin, vous pouvez spécifier le type intégral sous-jacent des énumérations C++ 11:

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

Auparavant, le type sous-jacent n'était pas spécifié, ce qui pourrait entraîner des problèmes de compatibilité entre plates-formes. Edit Il a été souligné dans les commentaires que vous pouvez également spécifier le type intégral sous-jacent d'un enum "ancien style" en C++ 11.

108
juanchopanza

Il y a un bon article sur les enums sur cette page IBM , il est très détaillé et bien écrit. Voici quelques points importants en un mot:

Les énumérations de portée résolvent la plupart des limitations imposées par les énumérations habituelles: sécurité du type complet, type sous-jacent bien défini, problèmes de portée et déclaration aval.

  • Vous obtenez la sécurité de type en refusant toutes les conversions implicites d'énums étendus en d'autres types.
  • Vous obtenez une nouvelle portée, et l'énumération n'est plus dans la portée englobante, ce qui évite les conflits de noms.
  • Scoped enums vous permet de spécifier le type sous-jacent de l'énumération. Pour les énumérations étendues, il est défini par défaut sur int si vous choisissez de ne pas le spécifier.
  • Toute énumération avec un type sous-jacent fixe peut être déclarée en avant.
15
SingerOfTheFall

Valeurs de enum class est vraiment de type enum class, ne pas underlying_type comme pour le C-enum.

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);
9
ForEveR

Les classes enum ("new enums", "strong enums") résolvent trois problèmes avec les énumérations C++ traditionnelles:

  1. conventionnel enums convertit implicitement en int, provoquant des erreurs lorsque quelqu'un ne veut pas qu'une énumération agisse comme un entier.
  2. conventionnel enums exporte ses énumérateurs vers la portée environnante, ce qui provoque des conflits de noms.
  3. Le type sous-jacent d'un enum ne peut pas être spécifié, ce qui entraîne une confusion, des problèmes de compatibilité et rend impossible la déclaration en aval.

enum class ("strong enums") sont fortement typés et étendus:

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

Comme indiqué, les enums traditionnels fonctionnent comme d'habitude, mais vous pouvez maintenant éventuellement vous qualifier avec le nom de l'énum.

Les nouveaux enum sont "enum class" car ils combinent des aspects des énumérations traditionnelles (valeurs de noms) avec des aspects des classes (membres définis et absence de conversions).

Etre capable de spécifier le type sous-jacent permet une interopérabilité plus simple et des tailles d'énumérations garanties:

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

Il permet également la déclaration en aval des énumérations:

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

Le type sous-jacent doit être l'un des types entiers signés ou non signés; la valeur par défaut est int.

Dans la bibliothèque standard, les classes enum sont utilisées pour:

  1. Codes d'erreur spécifiques aux systèmes de mappage: In <system_error>: enum class errc;
  2. Indicateurs de sécurité du pointeur: In <memory>: enum class pointer_safety { relaxed, preferred, strict };
  3. Erreurs de flux d'E/S: In <iosfwd>: enum class io_errc { stream = 1 };
  4. Traitement des erreurs de communications asynchrones: In <future>: enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Plusieurs d’entre eux ont des opérateurs, tels que == défini.

4
Jnana

Enum Scope

Les énumérations exportent leurs énumérateurs vers la portée environnante. Cela a deux inconvénients. Premièrement, cela peut conduire à des conflits de noms, si deux énumérateurs de différentes énumérations déclarées dans la même étendue ont le même nom; deuxièmement, il n'est pas possible d'utiliser un énumérateur avec un nom complet, y compris le nom enum.

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

select = ESet::a; // error
select = a;       // is ambigious
3
Ram