À quoi sert un enum
Une struct regroupe plusieurs valeurs liées dans un seul objet. Un enum résout un problème différent : il donne des noms à un petit ensemble fixe de choix. Au lieu de retenir que 0 signifie « rouge », 1 « vert » et 2 « bleu », vous écrivez Color::Red et le compilateur vous garde honnête.
Recourir à un enum dès qu'une variable ne peut prendre qu'un état nommé parmi une poignée — la couleur d'un feu de circulation, l'enseigne d'une carte, un statut de connexion — rend le code auto-documenté et permet au compilateur de repérer les fautes de frappe et les cas manquants que de simples entiers ne détecteraient jamais.
Déclarer un enum
Le C++ moderne propose deux variantes. Commencez par celle vers laquelle vous devriez vous tourner presque toujours : l'enum class à portée. Vous listez les noms, et chaque énumérateur s'accède via le nom de l'enum avec :: :
Remarquez que vous écrivez Color::Green, jamais un Green tout court. Les valeurs elles-mêmes ne sont que des étiquettes : vous les comparez, les assignez et les passez de l'une à l'autre, mais le nombre sous-jacent vous importe rarement. Par défaut Red vaut 0, Green vaut 1 et Blue vaut 2, en comptant à partir de zéro.
enum simple vs enum class
L'ancien enum sans portée (sans le mot-clé class) déverse ses noms directement dans la portée environnante et se convertit en int de lui-même. Cela semble pratique, mais cela cause deux vrais problèmes :
enum Color { Red, Green, Blue };
enum Fruit { Apple, Banana, Red }; // error: 'Red' already declared
enum Status { Active, Inactive };
int x = Active; // compiles silently - is this what you meant?
if (Active == Banana) { // compares unrelated enums via int - allowed!
}
Comme les énumérateurs simples sont des noms globaux, deux enums peuvent entrer en collision juste en partageant une étiquette. Et comme ils se dégradent en int, le compilateur compare sans broncher des valeurs d'enums totalement indépendants. Un enum class à portée corrige les deux : les noms vivent à l'intérieur du type, et le type ne se transformera pas silencieusement en int :
La règle empirique : utilisez enum class par défaut. Ne revenez à un enum simple que lorsque vous voulez spécifiquement la conversion implicite en int, comme pour les anciennes constantes de drapeaux à la mode C.
Valeurs personnalisées et type sous-jacent
Vous pouvez assigner des nombres explicites aux énumérateurs. Ceux que vous laissez de côté continuent de compter à partir du précédent, ce qui est pratique pour des choses comme les codes de statut HTTP ou les drapeaux de bits :
Chaque enum repose sur un type entier — int par défaut. Vous pouvez le fixer à un type plus petit quand la taille vous importe, par exemple pour stocker de nombreux enums dans une structure compacte ou pour correspondre à un format de transmission :
Choisir le type sous-jacent garantit aussi la plage dans laquelle vos valeurs doivent tenir : un enum uint8_t ne peut pas contenir une valeur supérieure à 255.
Convertir entre enum et int
Un enum class à portée ne se convertit jamais implicitement, et c'est tout l'intérêt. Quand vous avez réellement besoin du nombre — pour l'afficher, indexer un tableau ou le lire depuis un fichier — recourez à static_cast. Passer d'enum à int est toujours sûr :
Convertir un int de nouveau en enum est la direction dangereuse. La conversion ne vérifie pas que le nombre correspond à un énumérateur réel : elle vous remettra une valeur techniquement hors de l'ensemble nommé de l'enum :
Suit s = static_cast<Suit>(2); // fine - that's Clubs
Suit bad = static_cast<Suit>(99); // compiles, but 99 is not a valid Suit
// using `bad` in a switch or as an array index is a lurking bug
Si l'entier vient d'une saisie utilisateur ou d'un fichier, validez vous-même la plage avant de convertir, sinon vous créerez une valeur qu'aucun case ne traite jamais — une source subtile de comportement indéfini plus loin.
Utiliser les enums avec switch
Comme un enum est « l'un d'un ensemble fixe », il se marie parfaitement avec un switch. Quand vous couvrez chaque énumérateur, beaucoup de compilateurs vous avertiront si vous ajoutez plus tard une nouvelle valeur en oubliant de la gérer — une sécurité gratuite que les entiers bruts ne vous offrent pas :
Un piège : il n'existe pas de moyen intégré d'afficher le nom d'un énumérateur. cout << TrafficLight::Red ne compilera pas pour un enum à portée, et même pour un enum simple, cela affiche le nombre, pas « Red ». Un petit switch ou une table de correspondance comme celle ci-dessus est la façon habituelle de transformer un enum en chaîne lisible par un humain.
Suivant : Les exceptions
Les enums et les structs vous permettent de modéliser à quoi ressemblent vos données. Mais les vrais programmes doivent aussi gérer les choses qui tournent mal — un fichier qui ne s'ouvre pas, un nombre qui ne s'analyse pas, une valeur hors plage. Le C++ gère ces chemins d'échec avec les exceptions, et c'est le sujet de la page suivante.
Questions fréquentes
Quelle est la différence entre enum et enum class en C++ ?
Un enum simple laisse fuir ses noms dans la portée environnante et se convertit implicitement en int, ce qui provoque des collisions de noms et des comparaisons accidentelles. Un enum class à portée garde ses noms à l'intérieur de l'enum (Color::Red) et refuse de se convertir en int sans conversion explicite. Préférez enum class en C++ moderne : il est sûr en types et évite les pièges classiques.
Comment convertir un enum C++ en int ?
Un enum simple se convertit implicitement, donc int n = Red; fonctionne tel quel. Un enum class à portée exige une conversion explicite : int n = static_cast<int>(Color::Red);. Pour faire l'inverse, reconvertissez l'int : Color c = static_cast<Color>(2);, mais attention : l'exécution ne vérifie pas que la valeur correspond à un énumérateur valide.
À quelle valeur commence le premier enum en C++ ?
Par défaut, le premier énumérateur vaut 0, et chacun des suivants vaut un de plus que le précédent. Ainsi dans enum class Level { Low, Mid, High };, Low vaut 0, Mid vaut 1 et High vaut 2. Vous pouvez assigner des valeurs explicites à n'importe lequel d'entre eux pour modifier ce comportement.