Namespaces
Variants

Enumeration declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Une énumération est un type distinct dont la valeur est restreinte à une plage de valeurs (voir ci-dessous pour plus de détails), qui peut inclure plusieurs constantes explicitement nommées (" énumérateurs ").

Les valeurs des constantes sont des valeurs d'un type intégral connu comme le type sous-jacent de l'énumération. Une énumération a la même taille , représentation des valeurs , et exigences d'alignement que son type sous-jacent. De plus, chaque valeur d'une énumération a la même représentation que la valeur correspondante du type sous-jacent.

Une énumération est (re)déclarée en utilisant la syntaxe suivante :

enum-key attr  (optionnel) enum-head-name  (optionnel) enum-base  (optionnel)
{ enumerator-list  (optionnel) }
(1)
enum-key attr  (optionnel) enum-head-name  (optionnel) enum-base  (optionnel)
{ enumerator-list , }
(2)
enum-key attr  (optionnel) enum-head-name enum-base  (optionnel) ; (3) (depuis C++11)
1) enum-specifier , qui apparaît dans decl-specifier-seq de la syntaxe de déclaration : définit le type énumération et ses énumérateurs.
2) Une virgule finale peut suivre la enumerator-list .
3) Déclaration de type enum opaque : définit le type énumération mais pas ses énumérateurs : après cette déclaration, le type est un type complet et sa taille est connue.
enum-key -

enum

(jusqu'en C++11)

l'un des enum , enum class , ou enum struct

(depuis C++11)
attr - (depuis C++11) séquence facultative d'un nombre quelconque d' attributs
enum-head-name -

le nom de l'énumération en cours de déclaration, il peut être omis.

(jusqu'en C++11)

le nom de l'énumération en cours de déclaration, éventuellement précédé d'un spécificateur-de-nom-imbriqué : séquence de noms et d'opérateurs de résolution de portée :: , se terminant par un opérateur de résolution de portée. Il ne peut être omis que dans les déclarations d'énumération non scopées non opaques.
spécificateur-de-nom-imbriqué ne peut apparaître que si le nom de l'énumération est présent et que cette déclaration est une redéclaration. Pour les déclarations d'énumération opaques, spécificateur-de-nom-imbriqué ne peut apparaître qu'avant le nom de l'énumération dans les déclarations de spécialisation explicite .
Si spécificateur-de-nom-imbriqué est présent, le spécificateur-d'énumération ne peut pas se référer à une énumération simplement héritée ou introduite par une using déclaration , et le spécificateur-d'énumération ne peut apparaître que dans un espace de noms englobant la déclaration précédente. Dans de tels cas, spécificateur-de-nom-imbriqué ne peut pas commencer par un spécificateur decltype .

(depuis C++11)
enum-base - (depuis C++11) deux-points ( : ), suivi d'un séquence-de-spécificateurs-de-type qui nomme un type intégral (s'il est qualifié cv, les qualifications sont ignorées) qui servira de type sous-jacent fixe pour ce type d'énumération
enumerator-list - liste séparée par des virgules de définitions d'énumérateurs, chacune étant soit simplement un identifiant unique, qui devient le nom de l'énumérateur, soit un identifiant unique avec une expression constante : identifiant = expression-constante . Dans les deux cas, l' identifiant peut être directement suivi d'une séquence de spécificateurs d'attributs facultative. (depuis C++17)

Il existe deux types distincts d'énumérations : l'énumération non-scopée (déclarée avec le enum-key enum ) et l'énumération scopée (déclarée avec le enum-key enum class ou enum struct ).

Table des matières

Énumérations non délimitées

enum nom  (optionnel) { énumérateur = expression-constante , énumérateur = expression-constante , ... } (1)
enum nom  (optionnel) : type { énumérateur = expression-constante , énumérateur = expression-constante , ... } (2) (depuis C++11)
enum nom : type ; (3) (depuis C++11)
1) Déclare un type d'énumération non délimitée dont le type sous-jacent n'est pas fixe (dans ce cas, le type sous-jacent est un type intégral défini par l'implémentation qui peut représenter toutes les valeurs des énumérateurs ; ce type n'est pas plus grand que int sauf si la valeur d'un énumérateur ne peut pas être contenue dans un int ou un unsigned int . Si la enumerator-list est vide, le type sous-jacent est comme si l'énumération avait un seul énumérateur avec la valeur 0 . Si aucun type intégral ne peut représenter toutes les valeurs des énumérateurs, l'énumération est mal formée).
2) Déclare un type d'énumération non délimitée dont le type sous-jacent est fixe.
3) La déclaration opaque d'énumération pour une énumération non délimitée doit spécifier le nom et le type sous-jacent.

Chaque énumérateur devient une constante nommée du type de l'énumération (c'est-à-dire, name ), visible dans la portée englobante, et peut être utilisé partout où des constantes sont requises.

enum Color { red, green, blue };
Color r = red;
switch(r)
{
    case red  : std::cout << "rouge\n";   break;
    case green: std::cout << "vert\n";  break;
    case blue : std::cout << "bleu\n";  break;
}

Chaque énumérateur est associé à une valeur du type sous-jacent. Lorsque = sont fournis dans une enumerator-list , les valeurs des énumérateurs sont définies par ces constant-expression s associées. Si le premier énumérateur n'a pas de = , la valeur associée est zéro. Pour tout autre énumérateur dont la définition n'a pas de = , la valeur associée est la valeur de l'énumérateur précédent plus un.

enum Foo { a, b, c = 10, d, e = 1, f, g = f + c };
//a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12

Le nom d'une énumération non délimitée peut être omis : une telle déclaration introduit uniquement les énumérateurs dans la portée englobante :

enum { a, b, c = 0, d = a + 2 }; // définit a = 0, b = 1, c = 0, d = 2

Lorsqu'une énumération non délimitée est un membre de classe, ses énumérateurs peuvent être accédés en utilisant les opérateurs d'accès aux membres de classe . et -> :

struct X
{
    enum direction { left = 'l', right = 'r' };
};
X x;
X* p = &x;
int a = X::direction::left; // autorisé uniquement en C++11 et versions ultérieures
int b = X::left;
int c = x.left;
int d = p->left;

Dans les spécificateurs de déclaration d'une déclaration de membre , la séquence

enum enum-head-name :

est toujours analysée comme faisant partie d'une déclaration d'énumération :

struct S
{
    enum E1 : int {};
    enum E1 : int {}; // error: redeclaration of enumeration,
                      // NOT parsed as a zero-length bit-field of type enum E1
};
enum E2 { e1 };
void f()
{
    false ? new enum E2 : int(); // OK: 'int' is NOT parsed as the underlying type
}
(depuis C++11)

Nom d'énumération à des fins de liaison

Une énumération sans nom qui ne possède pas de typedef name for linkage purposes et qui a un énumérateur est désignée, pour les linkage purposes , par son type sous-jacent et son premier énumérateur ; une telle énumération est dite avoir un énumérateur comme name for linkage purposes .

Énumérations délimitées

enum struct|class nom { énumérateur = expression-constante , énumérateur = expression-constante , ... } (1)
enum struct|class nom : type { énumérateur = expression-constante , énumérateur = expression-constante , ... } (2)
enum struct|class nom ; (3)
enum struct|class nom : type ; (4)
1) déclare un type d'énumération délimitée dont le type sous-jacent est int (les mots-clés class et struct sont exactement équivalents)
2) déclare un type d'énumération délimitée dont le type sous-jacent est type
3) déclaration opaque d'énumération pour une énumération délimitée dont le type sous-jacent est int
4) déclaration opaque d'énumération pour une énumération délimitée dont le type sous-jacent est type

Chaque énumérateur devient une constante nommée du type de l'énumération (c'est-à-dire nom ), qui est contenue dans la portée de l'énumération, et peut être accédée en utilisant l'opérateur de résolution de portée. Il n'y a pas de conversions implicites des valeurs d'un énumérateur délimité vers les types entiers, bien que static_cast puisse être utilisé pour obtenir la valeur numérique de l'énumérateur.

#include <iostream>
int main()
{
    enum class Color { red, green = 20, blue };
    Color r = Color::blue;
    switch(r)
    {
        case Color::red  : std::cout << "red\n";   break;
        case Color::green: std::cout << "green\n"; break;
        case Color::blue : std::cout << "blue\n";  break;
    }
    // int n = r; // erreur : pas de conversion implicite de l'énumération délimitée vers int
    int n = static_cast<int>(r); // OK, n = 21
    std::cout << n << '\n'; // affiche 21
}
(depuis C++11)

Une énumération peut être initialisée à partir d'un entier sans cast, en utilisant l'initialisation par liste , si toutes les conditions suivantes sont remplies :

  • L'initialisation est une initialisation directe par liste.
  • La liste d'initialisation n'a qu'un seul élément.
  • L'énumération est soit délimitée (scoped) soit non délimitée avec un type sous-jacent fixe.
  • La conversion n'est pas rétrécissante (non-narrowing).

Cela permet d'introduire de nouveaux types entiers (par exemple SafeInt ) qui bénéficient des mêmes conventions d'appel existantes que leurs types entiers sous-jacents, même sur les ABI qui pénalisent le passage/retour de structures par valeur.

enum byte : unsigned char {}; // byte is a new integer type; see also std::byte (C++17)
byte b{42};        // OK as of C++17 (direct-list-initialization)
byte c = {42};     // error
byte d = byte{42}; // OK as of C++17; same value as b
byte e{-1};        // error
struct A { byte b; };
A a1 = {{42}};     // error (copy-list-initialization of a constructor parameter)
A a2 = {byte{42}}; // OK as of C++17
void f(byte);
f({42}); // error (copy-list-initialization of a function parameter)
enum class Handle : std::uint32_t { Invalid = 0 };
Handle h{42}; // OK as of C++17
(depuis C++17)


using enum déclaration

using enum using-enum-declarator ; (depuis C++20)
declarator - un (éventuellement qualifié) identifiant ou identifiants de template simples


declarator doit nommer un type d'énumération non dépendant . Les déclarations d'énumération sont trouvées par recherche ordinaire uniquement par type qualifiée ou non qualifiée , selon que declarator est qualifié ou non.

enum E { x };
void f()
{
    int E;
    using enum E; // OK
}
using F = E;
using enum F; // OK
template<class T>
using EE = T;
void g()
{
    using enum EE<E>; // OK
}

Une déclaration using enum introduit les noms des énumérateurs de l'énumération nommée comme si par une using déclaration pour chaque énumérateur. Lorsqu'elle est dans la portée d'une classe, une déclaration using enum ajoute les énumérateurs de l'énumération nommée comme membres à la portée, les rendant accessibles pour la recherche de membres.

enum class fruit { orange, apple };
struct S
{
    using enum fruit; // OK: introduit orange et apple dans S
};
void f()
{
    S s;
    s.orange;  // OK: nomme fruit::orange
    S::orange; // OK: nomme fruit::orange
}

Deux déclarations using enum qui introduisent deux énumérateurs du même nom entrent en conflit.

enum class fruit { orange, apple };
enum class color { red, orange };
void f()
{
    using enum fruit;    // OK
    // using enum color; // erreur: color::orange et fruit::orange entrent en conflit
}
(depuis C++20)

Notes

Les valeurs de type énumération non délimitée peuvent être promues ou converties en types entiers :

enum color { red, yellow, green = 20, blue };
color col = red;
int n = blue; // n == 21

Les valeurs des types entiers, à virgule flottante et d'énumération peuvent être converties en n'importe quel type d'énumération en utilisant static_cast . Notez que la valeur après une telle conversion peut ne pas nécessairement correspondre à l'un des énumérateurs nommés définis pour l'énumération :

enum access_t { read = 1, write = 2, exec = 4 }; // énumérateurs : 1, 2, 4 plage : 0..7
access_t rwe = static_cast<access_t>(7);
assert((rwe & read) && (rwe & write) && (rwe & exec));
access_t x = static_cast<access_t>(8.0); // comportement indéfini depuis CWG 1766
access_t y = static_cast<access_t>(8);   // comportement indéfini depuis CWG 1766
enum foo { a = 0, b = UINT_MAX }; // plage : [0, UINT_MAX]
foo x = foo(-1); // comportement indéfini depuis CWG 1766,
                 // même si le type sous-jacent de foo est unsigned int
Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_enumerator_attributes 201411L (C++17) Attributs pour les énumérateurs
__cpp_using_enum 201907L (C++20) using enum

Mots-clés

enum , struct , class , using

Exemple

#include <cstdint>
#include <iostream>
// enum qui prend 16 bits
enum smallenum: std::int16_t
{
    a,
    b,
    c
};
// color peut être red (valeur 0), yellow (valeur 1), green (valeur 20), ou blue (valeur 21)
enum color
{
    red,
    yellow,
    green = 20,
    blue
};
// altitude peut être altitude::high ou altitude::low
enum class altitude: char
{
    high = 'h',
    low = 'l', // virgule finale uniquement autorisée après CWG 518
}; 
// la constante d est 0, la constante e est 1, la constante f est 3
enum
{
    d,
    e,
    f = e + 2
};
// les types énumération (scopés et non scopés) peuvent avoir des opérateurs surchargés
std::ostream& operator<<(std::ostream& os, color c)
{
    switch(c)
    {
        case red   : os << "red";    break;
        case yellow: os << "yellow"; break;
        case green : os << "green";  break;
        case blue  : os << "blue";   break;
        default    : os.setstate(std::ios_base::failbit);
    }
    return os;
}
std::ostream& operator<<(std::ostream& os, altitude al)
{
    return os << static_cast<char>(al);
}
// L'énumération scopée (C++11) peut être partiellement émulée dans les révisions antérieures de C++ :
enum struct E11 { x, y }; // depuis C++11
struct E98 { enum { x, y }; }; // OK en pré-C++11
namespace N98 { enum { x, y }; } // OK en pré-C++11
struct S98 { static const int x = 0, y = 1; }; // OK en pré-C++11
void emu()
{
    std::cout << (static_cast<int>(E11::y) + E98::y + N98::y + S98::y) << '\n'; // 4
}
namespace cxx20
{
    enum class long_long_long_name { x, y };
    void using_enum_demo()
    {
        std::cout << "C++20 `using enum`: __cpp_using_enum == ";
        switch (auto rnd = []{return long_long_long_name::x;}; rnd())
        {
#if defined(__cpp_using_enum)
            using enum long_long_long_name;
            case x: std::cout << __cpp_using_enum << "; x\n"; break;
            case y: std::cout << __cpp_using_enum << "; y\n"; break;
#else
            case long_long_long_name::x: std::cout << "?; x\n"; break;
            case long_long_long_name::y: std::cout << "?; y\n"; break;
#endif
        }
    }
}
int main()
{
    color col = red;
    altitude a;
    a = altitude::low;
    std::cout << "col = " << col << '\n'
              << "a = "   << a   << '\n'
              << "f = "   << f   << '\n';
    cxx20::using_enum_demo();
}

Sortie possible :

col = rouge
a = l
f = 3
C++20 `using enum`: __cpp_using_enum == 201907; x

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement tel que publié Comportement correct
CWG 377 C++98 le comportement n'était pas spécifié lorsqu'aucun type
entier ne peut représenter toutes les valeurs d'énumérateur
l'énumération est mal
formée dans ce cas
CWG 518 C++98 une virgule finale n'était pas autorisée après la liste d'énumérateurs autorisée
CWG 1514 C++11 une redéfinition d'énumération avec type sous-jacent fixe
pouvait être analysée comme un champ de bits dans une déclaration de membre de classe
toujours analysée comme une redéfinition
CWG 1638 C++11 la grammaire de déclaration d'énumération opaque
interdisait l'utilisation pour les spécialisations de template
spécificateur de nom imbriqué
autorisé
CWG 1766 C++98 convertir une valeur hors plage en énumération
sans type sous-jacent fixe avait un résultat non spécifié
le comportement est indéfini
CWG 1966 C++11 la résolution de CWG issue 1514 faisait que le :
d'une expression conditionnelle faisait partie de enum-base
appliquer la résolution uniquement aux
spécificateurs de déclaration de membre
CWG 2156 C++11 les définitions d'énumération pouvaient définir
des types d'énumération via des using-declarations
interdit
CWG 2157 C++11 la résolution de CWG issue 1966 ne
couvrait pas les noms d'énumération qualifiés
couvert
CWG 2530 C++98 une liste d'énumérateurs pouvait contenir plusieurs
énumérateurs avec le même identifiant
interdit
CWG 2590 C++98 la taille, la représentation des valeurs et les exigences d'alignement
d'une énumération ne dépendaient pas de son type sous-jacent
tous sont identiques à
ceux du type sous-jacent
CWG 2621 C++20 la recherche de nom d'énumération utilisée dans
using enum les déclarations n'était pas claire
clarifiée
CWG 2877 C++20 la recherche de nom d'énumération utilisée dans
using enum les déclarations n'était pas uniquement de type
rendue uniquement de type

Références

  • Norme C++23 (ISO/CEI 14882:2024) :
  • 9.7.1 Déclarations d'énumération [dcl.enum]
  • Norme C++20 (ISO/CEI 14882:2020) :
  • 9.7.1 Déclarations d'énumération [dcl.enum]
  • Norme C++17 (ISO/IEC 14882:2017) :
  • 10.2 Déclarations d'énumération [dcl.enum]
  • Norme C++14 (ISO/IEC 14882:2014) :
  • 7.2 Déclarations d'énumération [dcl.enum]
  • Norme C++11 (ISO/IEC 14882:2011) :
  • 7.2 Déclarations d'énumération [dcl.enum]
  • Norme C++03 (ISO/CEI 14882:2003) :
  • 7.2 Déclarations d'énumération [dcl.enum]
  • Norme C++98 (ISO/CEI 14882:1998) :
  • 7.2 Déclarations d'énumération [dcl.enum]

Voir aussi

(C++11)
vérifie si un type est un type énumération
(modèle de classe)
vérifie si un type est un type énumération délimitée
(modèle de classe)
obtient le type entier sous-jacent pour un type énumération donné
(modèle de classe)
convertit une énumération en son type sous-jacent
(modèle de fonction)
Documentation C pour Énumérations