Souvent, il faut plusieurs types énumérés ensemble. Parfois, on a un conflit de nom. Deux solutions me viennent à l'esprit: utilisez un espace de noms ou utilisez des noms d'élément d'énumération «plus grands». Pourtant, la solution d'espace de noms a deux implémentations possibles: une classe factice avec une énumération imbriquée ou un espace de noms complet.
Je recherche les avantages et les inconvénients des trois approches.
Exemple:
// oft seen hand-crafted name clash solution
enum eColors { cRed, cColorBlue, cGreen, cYellow, cColorsEnd };
enum eFeelings { cAngry, cFeelingBlue, cHappy, cFeelingsEnd };
void setPenColor( const eColors c ) {
switch (c) {
default: assert(false);
break; case cRed: //...
break; case cColorBlue: //...
//...
}
}
// (ab)using a class as a namespace
class Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
class Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
void setPenColor( const Colors::e c ) {
switch (c) {
default: assert(false);
break; case Colors::cRed: //...
break; case Colors::cBlue: //...
//...
}
}
// a real namespace?
namespace Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
namespace Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
void setPenColor( const Colors::e c ) {
switch (c) {
default: assert(false);
break; case Colors::cRed: //...
break; case Colors::cBlue: //...
//...
}
}
enum e {...}
, les énumérations peuvent être anonymes, c'estenum {...}
-à- dire , ce qui a beaucoup plus de sens lorsqu'elles sont enveloppées dans un espace de noms ou une classe.Réponses:
Réponse originale C ++ 03:
L' avantage d'un
namespace
(sur aclass
) est que vous pouvez utiliser desusing
déclarations quand vous le souhaitez.Le problème avec l'utilisation de a
namespace
est que les espaces de noms peuvent être développés ailleurs dans le code. Dans un grand projet, vous ne seriez pas assuré que deux énumérations distinctes ne pensent pas toutes deux qu'elles sont appeléeseFeelings
Pour un code plus simple, j'utilise a
struct
, car vous voulez probablement que le contenu soit public.Si vous pratiquez l'une de ces pratiques, vous êtes en avance sur la courbe et n'avez probablement pas besoin d'examiner cela plus en détail.
Plus récent, conseil C ++ 11:
Si vous utilisez C ++ 11 ou version ultérieure,
enum class
portera implicitement les valeurs d'énumération dans le nom de l'énumération.Avec,
enum class
vous perdrez les conversions implicites et les comparaisons avec les types entiers, mais en pratique, cela peut vous aider à découvrir du code ambigu ou bogué.la source
FYI En C ++ 0x, il y a une nouvelle syntaxe pour des cas comme ce que vous avez mentionné (voir la page wiki C ++ 0x )
la source
J'ai hybridé les réponses précédentes à quelque chose comme ceci: (EDIT: Ceci n'est utile que pour les pré-C ++ 11. Si vous utilisez C ++ 11, utilisez
enum class
)J'ai un gros fichier d'en-tête qui contient toutes les énumérations de mon projet, car ces énumérations sont partagées entre les classes de travail et cela n'a pas de sens de mettre les énumérations dans les classes de travail elles-mêmes.
Le
struct
évite le public: sucre syntaxique, et letypedef
vous permet de déclarer les variables de ces énumérations dans d'autres classes de travail.Je ne pense pas que l'utilisation d'un espace de noms aide du tout. C'est peut-être parce que je suis un programmeur C #, et là, vous devez utiliser le nom de type enum pour référencer les valeurs, donc je suis habitué.
...
la source
J'éviterais certainement d'utiliser une classe pour cela; utilisez plutôt un espace de noms. La question se résume à savoir s'il faut utiliser un espace de noms ou utiliser des identifiants uniques pour les valeurs d'énumération. Personnellement, j'utiliserais un espace de noms pour que mes identifiants soient plus courts et, espérons-le, plus explicites. Ensuite, le code de l'application pourrait utiliser une directive 'using namespace' et rendre tout plus lisible.
D'après votre exemple ci-dessus:
la source
Colors someColor = Red;
, car l'espace de noms ne constitue pas un type. Vous auriez à écrire à laColors::e someColor = Red;
place, ce qui est assez contre-intuitif.Colors::e someColor
même avec unstruct/class
si vous vouliez l'utiliser dans uneswitch
déclaration? Si vous utilisez un anonyme,enum
le commutateur ne pourra pas évaluer un fichierstruct
.const e c
me semble difficilement lisible :-) Ne fais pas ça. L'utilisation d'un espace de noms est cependant très bien.Une différence entre l'utilisation d'une classe ou d'un espace de noms est que la classe ne peut pas être rouverte comme un espace de noms peut. Cela évite la possibilité que l'espace de noms puisse être abusé à l'avenir, mais il y a aussi le problème que vous ne pouvez pas non plus ajouter à l'ensemble des énumérations.
Un avantage possible de l'utilisation d'une classe est qu'elle peut être utilisée comme arguments de type modèle, ce qui n'est pas le cas pour les espaces de noms:
Personnellement, je ne suis pas fan de l' utilisation , et je préfère les noms complets, donc je ne vois pas vraiment cela comme un plus pour les espaces de noms. Cependant, ce n'est probablement pas la décision la plus importante que vous ferez dans votre projet!
la source
L'avantage d'utiliser une classe est que vous pouvez créer une classe à part entière par-dessus.
Comme le montre l'exemple ci-dessus, en utilisant une classe, vous pouvez:
Notez simplement que vous devez déclarer
operator enum_type()
pour que C ++ sache comment convertir votre classe en énumération sous-jacente. Sinon, vous ne pourrez pas transmettre le type à uneswitch
instruction.la source
Puisque les énumérations sont étendues à leur portée englobante, il est probablement préférable de les envelopper dans quelque chose pour éviter de polluer l'espace de noms global et pour éviter les collisions de noms. Je préfère un espace de noms à la classe simplement parce que cela
namespace
ressemble à un sac de maintien, alors que jeclass
me sens comme un objet robuste (cf. le débatstruct
vsclass
). Un avantage possible d'un espace de noms est qu'il peut être étendu plus tard - utile si vous avez affaire à du code tiers que vous ne pouvez pas modifier.Tout cela est bien sûr inutile lorsque nous obtenons des classes enum avec C ++ 0x.
la source
J'ai aussi tendance à emballer mes énumérations dans les classes.
Comme l'a signalé Richard Corden, l'avantage d'une classe est qu'il s'agit d'un type au sens c ++ et que vous pouvez donc l'utiliser avec des modèles.
J'ai une classe spéciale toolbox :: Enum pour mes besoins que je spécialise pour tous les modèles qui fournissent des fonctions de base (principalement: mapper une valeur enum à une std :: string afin que les E / S soient plus faciles à lire).
Mon petit modèle a également l'avantage supplémentaire de vraiment vérifier les valeurs autorisées. Le compilateur est un peu laxiste pour vérifier si la valeur est vraiment dans l'énumération:
Cela m'a toujours dérangé que le compilateur n'attrape pas cela, car il vous reste une valeur enum qui n'a aucun sens (et à laquelle vous ne vous attendez pas).
De même:
Une fois de plus, main renverra une erreur.
Le problème est que le compilateur ajustera l'énumération dans la plus petite représentation disponible (ici nous avons besoin de 2 bits) et que tout ce qui rentre dans cette représentation est considéré comme une valeur valide.
Il y a aussi le problème que parfois vous préférez avoir une boucle sur les valeurs possibles au lieu d'un commutateur afin de ne pas avoir à modifier tous vos commutateurs chaque fois que vous ajoutez une valeur à l'énumération.
Dans l'ensemble, mon petit assistant facilite vraiment les choses pour mes énumérations (bien sûr, cela ajoute des frais généraux) et ce n'est possible que parce que j'imbrique chaque énumération dans sa propre structure :)
la source