Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Une énumération est un type défini par l’utilisateur qui se compose d’un ensemble de constantes intégrales nommées appelées énumérateurs.
Remarque
Cet article traite du type de langage enum
C++ ISO Standard et du type délimité (ou fortement typé) enum class
introduit en C++11. Pour plus d’informations sur les public enum class
types en C++/CLI et C++/CX, consultez enum class
(C++/CLI et C++/CX)private enum class
.
Syntaxe
enum-name
:
identifier
enum-specifier
:
enum-head
{
enumerator-list
opter}
enum-head
{
enumerator-list
,
}
enum-head
:
enum-key
attribute-specifier-seq
opterenum-head-name
opterenum-base
opter
enum-head-name
:
nested-name-specifier
optezidentifier
opaque-enum-declaration
:
enum-key
attribute-specifier-seq
opterenum-head-name
enum-base
opter;
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-seq
opter
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 n’importe quel type intégral.
enum-list
Liste séparée par des virgules des énumérateurs dans l’énumération. Chaque énumérateur ou nom de variable dans l’étendue doit être unique. Toutefois, les valeurs peuvent être dupliquées. Dans une énumération non portée, l’étendue est l’étendue environnante ; dans une énumération délimitée, l’étendue est la enum-list
même. Dans une énumération délimitée, la liste peut être vide, qui définit en effet un nouveau type intégral.
class
À l’aide de ce mot clé dans la déclaration, vous spécifiez l’étendue de l’énumération et un identifier
doit être fourni. Vous pouvez également utiliser le mot clé à la struct
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 C et C++ enum
d’origine, les énumérateurs non qualifiés sont visibles dans toute l’étendue dans laquelle le enum
fichier est déclaré. Dans les énumérations délimitées, le nom de l’énumérateur doit être qualifié par le nom du enum
type. L’exemple suivant illustre cette différence de base entre les deux types d’énumérations :
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 affecté à une valeur intégrale qui correspond à sa place dans l’ordre des valeurs de l’énumération. Par défaut, la première valeur est affectée à 0, la suivante est affectée à 1, et ainsi de suite, mais vous pouvez définir explicitement la valeur d’un énumérateur, comme illustré ici :
enum Suit { Diamonds = 1, Hearts, Clubs, Spades };
L’énumérateur Diamonds
est affecté à la valeur 1
. Les énumérateurs suivants, s’ils ne reçoivent pas de valeur explicite, reçoivent la valeur de l’énumérateur précédent plus un. Dans l’exemple précédent, Hearts
aurait la valeur 2, Clubs
aurait 3, et ainsi de suite.
Chaque énumérateur est traité comme une constante et doit avoir un nom unique dans l’étendue où l’énumération enum
est définie (pour les énumérations non portées) ou dans l’autre enum
(pour les énumérations délimitées). Les valeurs données aux noms n’ont pas besoin d’être uniques. Par exemple, considérez cette déclaration d’une énumération Suit
non étendue :
enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };
Les valeurs de Diamonds
, , Clubs
Hearts
, et Spades
sont respectivement 5, 6, 4 et 5. Notez que 5 est utilisé plusieurs fois ; elle est autorisée même si elle n’est peut-être pas prévue. Ces règles sont identiques pour les énumérations délimitées.
Règles de cast
Les constantes d’énumération non portées peuvent être converties implicitement en int
, mais une int
valeur d’énumération n’est jamais implicitement convertible. L’exemple suivant montre ce qui se passe si vous essayez d’affecter 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 cast est nécessaire pour convertir un int
énumérateur délimité ou non délimité. Toutefois, vous pouvez promouvoir un énumérateur nonscope en une valeur entière sans cast.
int account_num = Hearts; //OK if Hearts is in an unscoped enum
L’utilisation de conversions implicites de cette façon peut entraîner des effets secondaires inattendus. Pour éliminer les erreurs de programmation associées à des énumérations non portées, les valeurs d’énumération délimitées sont fortement typées. Les énumérateurs délimités doivent être qualifiés par le nom de type d’énumération (identificateur) et ne peuvent pas être convertis implicitement, comme illustré 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 qui se produit avec des énumérations non étendues, comme indiqué précédemment. Elle est autorisée avec un cast explicite. Toutefois, avec des énumérations délimitées, la tentative de conversion dans l’instruction suivante n’est account_num = Suit::Hearts;
plus autorisée sans cast 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 cast 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;
}