Partager via


Énumérations (C++)

Une énumération est un type défini par l'utilisateur qui se compose d'un jeu de constantes intégrales nommées, appelées énumérateurs.

Remarque

Cet article décrit le type enum du langage C++ de norme ISO et le type enum class délimité (ou fortement typé) qui a fait son apparition dans C++11. Pour plus d'informations sur les types public enum class ou private enum class en C++/CLI et C++/CX, consultez enum class (C++/CLI et C++/CX).

Syntaxe

enum-name:
identifier

enum-specifier:
enum-head {optenumerator-list }
enum-head { enumerator-list , }

enum-head:
enum-key attribute-specifier-seqopt enum-head-nameopt enum-baseopt

enum-head-name:
nested-name-specifieropt identifier

opaque-enum-declaration:
enum-key attribute-specifier-seqopt enum-head-name enum-baseopt ;

enum-key:
enum
enum class
enum struct

enum-base:
: type-specifier-seq

enumerator-list:
enumerator-definition
enumerator-list , enumerator-definition

enumerator-definition:
enumerator
enumerator = constant-expression

enumerator:
identifier attribute-specifier-seqopt

Utilisation

// unscoped enum:
// enum [identifier] [: type] {enum-list};

// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};

// Forward declaration of enumerations  (C++11):
enum A : int;          // non-scoped enum must have type specified
enum class B;          // scoped enum defaults to int but ...
enum class C : short;  // ... may have any integral underlying type

Paramètres

identifier
Nom de type donné à l'énumération.

type
Type sous-jacent des énumérateurs ; tous les énumérateurs ont le même type sous-jacent. Peut être tout type intégral.

enum-list
Liste délimitée par des virgules des énumérateurs dans l'énumération. Chaque nom d'énumérateur ou de variable dans la portée doit être unique. Toutefois, les valeurs peuvent être dupliquées. Dans un enum non délimité, la portée correspond à la portée environnante ; dans un enum délimité, la portée correspond à la liste enum-list proprement dite. Dans une énumération délimitée, la liste peut être vide, qui définit en effet un nouveau type intégral.

class
En utilisant ce mot clé dans la déclaration, vous spécifiez que l'enum est délimité et qu'un identifier doit être fourni. Vous pouvez aussi utiliser le mot clé struct à la place de class, car ils sont sémantiquement équivalents dans ce contexte.

Étendue de l’énumérateur

Une énumération fournit un contexte pour décrire une plage de valeurs représentées sous forme de constantes nommées. Ces constantes nommées sont également appelées énumérateurs. Dans les types enum C et C++ d'origine, les énumérateurs non qualifiés sont visibles dans toute la portée dans laquelle enum est déclaré. Dans les enums délimités, le nom de l'énumérateur doit être qualifié par le nom du type enum. L'exemple suivant illustre cette différence fondamentale entre les deux genres d'enums :

namespace CardGame_Scoped
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
        { /*...*/}
    }
}

namespace CardGame_NonScoped
{
    enum Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Clubs) // Enumerator is visible without qualification
        { /*...*/
        }
    }
}

À chaque nom d'une énumération est assignée une valeur entière qui correspond à sa place dans l'ordre des valeurs de l'énumération. Par défaut, la première valeur est 0, la suivante 1, et ainsi de suite, mais vous pouvez définir explicitement la valeur d'un énumérateur, comme indiqué ci-dessous :

enum Suit { Diamonds = 1, Hearts, Clubs, Spades };

L'énumérateur Diamonds reçoit la valeur 1. Les énumérateurs suivants, si aucune valeur explicite ne leur est assignée, reçoivent la valeur de l'énumérateur précédent incrémentée d'une unité. Dans l'exemple précédent, Hearts aurait la valeur 2, Clubs aurait la valeur 3, et ainsi de suite.

Chaque énumérateur est traité comme une constante et doit avoir un nom unique dans la portée où enum est défini (pour les enums non délimités) ou dans enum proprement dit (pour les enums délimités). Il n'est pas obligatoire que les valeurs fournies aux noms soient uniques. Par exemple, considérez cette déclaration d’une énumération Suit non délimitée :

enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };

Les valeurs de Diamonds, Hearts, Clubs et Spades sont respectivement 5, 6, 4 et 5. Notez que 5 est utilisé plusieurs fois ; cela est autorisé même si ce n'est peut-être pas prévu. Ces règles sont les mêmes pour les enums délimités.

Règles de transtypage

Les constantes enum non délimitées peuvent être implicitement converties en int, mais une valeur int n'est jamais implicitement convertie en valeur enum. L'exemple suivant montre ce qui se produit si vous essayez d'assigner à hand une valeur qui n'est pas un Suit :

int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

Un transtypage est nécessaire pour convertir une valeur int en énumérateur délimité ou non délimité. Toutefois, vous pouvez promouvoir un énumérateur non délimité en valeur entière sans transtypage.

int account_num = Hearts; //OK if Hearts is in an unscoped enum

L'utilisation des conversions implicites de cette façon peut provoquer des effets secondaires inattendus. Pour aider à éliminer les erreurs de programmation associées aux enums non délimités, les valeurs d'enums délimités sont fortement typées. Les énumérateurs délimités doivent être qualifiés par le nom du type d'enum (identificateur) et ne peuvent pas être implicitement convertis, comme indiqué dans l'exemple suivant :

namespace ScopedEnumConversions
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void AttemptConversions()
    {
        Suit hand;
        hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
        hand = Suit::Clubs; //Correct.
        int account_num = 135692;
        hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
        hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!

        account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
        account_num = static_cast<int>(Suit::Hearts); // OK
    }
}

Notez que la ligne hand = account_num; provoque toujours l'erreur avec les enums non délimités, comme indiqué précédemment. Elle est autorisée avec un transtypage explicite. Toutefois, avec les enums délimités, la tentative de conversion dans l'instruction suivante, account_num = Suit::Hearts;, n'est plus autorisée sans transtypage explicite.

Énumérations sans énumérateurs

Visual Studio 2017 version 15.3 et ultérieure (disponible avec /std:c++17 et versions ultérieures) : en définissant une énumération (régulière ou étendue) avec un type sous-jacent explicite et aucun énumérateur, vous pouvez introduire en effet un nouveau type intégral qui n’a aucune conversion implicite vers un autre type. En utilisant ce type au lieu de son type sous-jacent intégré, vous pouvez éliminer le risque d’erreurs subtiles causées par des conversions implicites par inadvertance.

enum class byte : unsigned char { };

Le nouveau type est une copie exacte du type sous-jacent, et a donc la même convention d’appel, ce qui signifie qu’il peut être utilisé entre les API sans pénalité de performances. Aucun diffuser n’est requis lorsque les variables du type sont initialisées à l’aide de l’initialisation de liste directe. L’exemple suivant montre comment initialiser des énumérations sans énumérateurs dans différents contextes :

enum class byte : unsigned char { };

enum class E : int { };
E e1{ 0 };
E e2 = E{ 0 };

struct X
{
    E e{ 0 };
    X() : e{ 0 } { }
};

E* p = new E{ 0 };

void f(E e) {};

int main()
{
    f(E{ 0 });
    byte i{ 42 };
    byte j = byte{ 42 };

    // unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char'
    return 0;
}

Voir aussi

Déclarations d’énumération C
Mots clés