Déclarer une énumération dans une classe


151

Dans l'extrait de code suivant, l' Colorénumération est déclarée dans la Carclasse afin de limiter la portée de l'énumération et d'essayer de ne pas «polluer» l'espace de noms global.

class Car
{
public:

   enum Color
   {
      RED,
      BLUE,
      WHITE
   };

   void SetColor( Car::Color color )
   {
      _color = color;
   }

   Car::Color GetColor() const
   {
      return _color;
   }

private:

   Car::Color _color;

};

(1) Est-ce un bon moyen de limiter la portée de l' Colorénumération? Ou devrais-je le déclarer en dehors de la Carclasse, mais éventuellement dans son propre espace de noms ou structure? Je viens de tomber sur cet article aujourd'hui, qui prône ce dernier et discute de quelques points intéressants sur les énumérations: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums .

(2) Dans cet exemple, lorsque vous travaillez au sein de la classe, est-il préférable de coder l'énumération comme Car::Color, ou Colorsuffirait-il? (Je suppose que le premier est meilleur, juste au cas où il y aurait une autre Colorénumération déclarée dans l'espace de noms global. De cette façon, au moins, nous sommes explicites sur l'énumération à laquelle nous faisons référence.)

Réponses:


86
  1. Si Colorc'est quelque chose qui est spécifique à juste Cars, c'est ainsi que vous limiteriez sa portée. Si vous allez avoir une autre Colorénumération que d'autres classes utilisent, vous pouvez aussi bien la rendre globale (ou du moins à l'extérieur Car).

  2. Ça ne fait aucune différence. S'il y en a un global, alors le local est toujours utilisé car il est plus proche de la portée actuelle. Notez que si vous définissez ces fonctions en dehors de la définition de classe, vous devrez les spécifier explicitement Car::Colordans l'interface de la fonction.


12
2. Oui et non. Car::Color getColor()mais void Car::setColor(Color c)parce que setColornous avons déjà le spécificateur.
Matthieu M.


66

Je préfère l'approche suivante (code ci-dessous). Il résout le problème de la "pollution des espaces de noms", mais il est aussi beaucoup plus sûr de type (vous ne pouvez pas assigner et même comparer deux énumérations différentes, ou votre énumération avec d'autres types intégrés, etc.).

struct Color
{
    enum Type
    {
        Red, Green, Black
    };
    Type t_;
    Color(Type t) : t_(t) {}
    operator Type () const {return t_;}
private:
   //prevent automatic conversion for any other built-in types such as bool, int, etc
   template<typename T>
    operator T () const;
};

Usage:

Color c = Color::Red;
switch(c)
{
   case Color::Red:
     //некоторый код
   break;
}
Color2 c2 = Color2::Green;
c2 = c; //error
c2 = 3; //error
if (c2 == Color::Red ) {} //error
If (c2) {} error

Je crée une macro pour faciliter l'utilisation:

#define DEFINE_SIMPLE_ENUM(EnumName, seq) \
struct EnumName {\
   enum type \
   { \
      BOOST_PP_SEQ_FOR_EACH_I(DEFINE_SIMPLE_ENUM_VAL, EnumName, seq)\
   }; \
   type v; \
   EnumName(type v) : v(v) {} \
   operator type() const {return v;} \
private: \
    template<typename T> \
    operator T () const;};\

#define DEFINE_SIMPLE_ENUM_VAL(r, data, i, record) \
    BOOST_PP_TUPLE_ELEM(2, 0, record) = BOOST_PP_TUPLE_ELEM(2, 1, record),

Usage:

DEFINE_SIMPLE_ENUM(Color,
             ((Red, 1))
             ((Green, 3))
             )

Quelques références:

  1. Herb Sutter, Jum Hyslop, Journal des utilisateurs C / C ++, 22 (5), mai 2004
  2. Herb Sutter, David E. Miller, Bjarne Stroustrup Strongly Typed Enums (révision 3), juillet 2007

J'aime ça. Cela force également l'énumération à être instanciée avec une valeur valide. Je pense qu'un opérateur d'affectation et un constructeur de copie seraient utiles. De plus, t_ doit être privé. Les macros dont je peux me passer.
jmucchiello

J'aime aussi ça. Merci pour les références.
anio

1
Vous avez dit: "aussi il est beaucoup plus sûr de type (vous ne pouvez pas attribuer et même comparer deux énumérations différentes ..." . Pourquoi pensez-vous que c'est une bonne fonctionnalité? Je pense que if(c2 == Color::Red )c'est raisonnable et doit compiler, mais dans votre exemple, il Même argument pour la cession aussi!
Nawaz

3
@Nawaz c2est d'un autre type ( Color2), alors pourquoi pensez-vous que les c2 == Color::Redaffectations devraient être compilées? Et si Color::Redvaut 1 et Color2::Red2? Devrait Color::Red == Color2::Redévaluer trueou false? Si vous mélangez des recenseurs non sécurisés, vous allez passer un mauvais moment.
Victor K

2
Pourquoi le type n'est-il pas t_; privé?
Zingam

7

En général, je mets toujours mes énumérations dans un fichier struct. J'ai vu plusieurs directives, y compris le "préfixage".

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

J'ai toujours pensé que cela ressemblait plus à des Cdirectives qu'à des directives C++(d'une part à cause de l'abréviation et aussi à cause des espaces de noms dansC++ ).

Donc, pour limiter la portée, nous avons maintenant deux alternatives:

  • espaces de noms
  • structures / classes

Personnellement, j'ai tendance à utiliser un structcar il peut être utilisé comme paramètres pour la programmation de modèles alors qu'un espace de noms ne peut pas être manipulé.

Des exemples de manipulation comprennent:

template <class T>
size_t number() { /**/ }

qui renvoie le nombre d'éléments d'énumération à l'intérieur de la structure T:)


3

Si vous créez une bibliothèque de codes, j'utiliserais un espace de noms. Cependant, vous ne pouvez toujours avoir qu'une seule énumération Color dans cet espace de noms. Si vous avez besoin d'une énumération qui pourrait utiliser un nom commun, mais qui pourrait avoir des constantes différentes pour différentes classes, utilisez votre approche.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.