Les énumérations sont utilisées pour définir des ensembles de valeurs entières et pour définir des types pour de tels ensembles de valeurs. Il existe deux sortes d’énumérations : les enum « classiques » et les enum class.
enum class aux enum « classiques »ALL_CAPS pour les énumérateursLes macros ne respectent pas les règles de portée et de typage. De plus, les noms de macros sont supprimés lors du pré‑traitement et n’apparaissent généralement pas dans les outils tels que les débogueurs.
Mauvais code historique :
// webcolors.h (third party header)
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
// productinfo.h
// The following define product subtypes based on color
#define RED 0
#define PURPLE 1
#define BLUE 2
int webby = BLUE; // webby == 2; probably not what was desired
Utilisez plutôt une enum :
enum class Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
enum class Product_info { red = 0, purple = 1, blue = 2 };
int webby = blue; // error: be specific
Web_color webby = Web_color::blue;
Nous avons utilisé un enum class pour éviter les conflits de noms.
Envisagez également les variables constexpr et const inline.
Signaler les macros qui définissent des valeurs entières. Utiliser une enum, ou const inline, ou une autre alternative non macro à la place.
Une énumération montre que les énumérateurs sont liés et peut être un type nommé.
enum class Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
Utiliser un switch sur une énumération est courant et le compilateur peut avertir contre des modèles de case inhabituels. Par exemple :
enum class Product_info { red = 0, purple = 1, blue = 2 };
void print(Product_info inf)
{
switch (inf) {
case Product_info::red: cout << "red"; break;
case Product_info::purple: cout << "purple"; break;
}
}
Ce type d’instructions switch avec un décalage d’un est souvent le résultat d’un ajout d’énumérateur et d’un manque de tests.
switch dont les case couvrent la plupart mais pas tous les énumérateurs d’une énumération.switch dont les case ne couvrent que quelques énumérateurs d’une énumération, sans default.enum class aux enum « classiques »Pour limiter les surprises : les enum traditionnels se convertissent trop facilement en int.
void Print_color(int color);
enum Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
enum Product_info { red = 0, purple = 1, blue = 2 };
Web_color webby = Web_color::blue;
// Clearly at least one of these calls is buggy.
Print_color(webby);
Print_color(Product_info::blue);
Utilisez plutôt un enum class :
void Print_color(int color);
enum class Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
enum class Product_info { red = 0, purple = 1, blue = 2 };
Web_color webby = Web_color::blue;
Print_color(webby); // Error: cannot convert Web_color to int.
Print_color(Product_info::red); // Error: cannot convert Product_info to int.
Avertir sur toute définition d’enum non class.
Facilité d’utilisation et prévention des erreurs.
enum class Day { mon, tue, wed, thu, fri, sat, sun };
Day& operator++(Day& d)
{
return d = (d == Day::sun) ? Day::mon : static_cast<Day>(static_cast<int>(d)+1);
}
Day today = Day::sat;
Day tomorrow = ++today;
L’utilisation d’un static_cast n’est pas très élégante, mais
Day& operator++(Day& d)
{
return d = (d == Day::sun) ? Day::mon : Day{++d}; // error
}
est une récursion infinie, et l’écrire sans cast en utilisant un switch couvrant tous les cas est lourd.
Signaler les expressions répétées qui sont à nouveau castées en une énumération.
ALL_CAPS pour les énumérateursÉviter les conflits avec les macros.
// webcolors.h (third party header)
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
// productinfo.h
// The following define product subtypes based on color
enum class Product_info { RED, PURPLE, BLUE }; // syntax error
Signaler les énumérateurs en ALL_CAPS.
Si vous ne pouvez pas nommer une énumération, les valeurs ne sont pas liées.
enum { red = 0xFF0000, scale = 4, is_signed = 1 };
Ce type de code n’est pas rare dans du code écrit avant qu’il ne soit possible de spécifier des constantes entières de manière pratique.
constexpr int red = 0xFF0000;
constexpr short scale = 4;
constexpr bool is_signed = true;
Signaler les énumérations non nommées.
Le type par défaut est le plus simple à lire et à écrire. int est le type entier par défaut. int est compatible avec les enum en C.
enum class Direction : char { n, s, e, w,
ne, nw, se, sw }; // underlying type saves space
enum class Web_color : int32_t { red = 0xFF0000,
green = 0x00FF00,
blue = 0x0000FF }; // underlying type is redundant
Spécifier le type sous‑jacent est nécessaire pour une déclaration anticipée (forward‑declare) d’un enum ou d’un enum class :
enum Flags : char;
void f(Flags);
// ....
enum Flags : char { /* ... */ };
ou pour garantir que les valeurs de ce type ont une précision binaire spécifiée :
enum Bitboard : uint64_t { /* ... */ };
????
C’est le plus simple. Cela évite les valeurs d’énumérateurs dupliquées. La valeur par défaut génère un ensemble de valeurs consécutives, ce qui est idéal pour les implémentations d’instructions switch.
enum class Col1 { red, yellow, blue };
enum class Col2 { red = 1, yellow = 2, blue = 2 }; // typo
enum class Month { jan = 1, feb, mar, apr, may, jun,
jul, august, sep, oct, nov, dec }; // starting with 1 is conventional
enum class Base_flag { dec = 1, oct = dec << 1, hex = dec << 2 }; // set of bits
Spécifier les valeurs est nécessaire pour correspondre aux valeurs conventionnelles (p. ex., Month) et lorsque des valeurs consécutives sont indésirables (p. ex., pour obtenir des bits séparés comme dans Base_flag).