web-dev-qa-db-fra.com

Déclarer une énumération dans une classe

Dans l'extrait de code suivant, l'énumération Color est déclarée dans la classe Car afin de limiter la portée de l'énum et d'éviter de "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'énum Color? Ou devrais-je le déclarer en dehors de la classe Car, mais éventuellement dans son propre espace de noms ou structure? Je viens de tomber sur cet article aujourd'hui, qui défend ce dernier et discute de quelques points de Nice concernant les enums: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums .

(2) Dans cet exemple, lorsque vous travaillez dans la classe, vaut-il mieux coder l'énum en tant que Car::Color, ou serait simplement Color suffire? (Je suppose que le premier est meilleur, juste au cas où il y aurait une autre Color énum déclarée dans l'espace de noms global. Ainsi, au moins, nous sommes explicites à propos de l'énum auquel nous faisons référence.)

137
bporter
  1. Si Color est quelque chose qui est spécifique à Cars, alors vous limitez sa portée. Si vous avez une autre énumération Color que d’autres classes utilisent, vous pouvez également la rendre globale (ou du moins en dehors de Car).

  2. Ça ne fait aucune différence. S'il y en a un global, 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 spécifier explicitement Car::Color dans l'interface de la fonction.

78
Peter Alexander

De nos jours - en utilisant C++ 11 - vous pouvez utiliser enum class pour cela:

enum class Color { RED, BLUE, WHITE };

AFAII cela fait exactement ce que vous voulez.

78
Andreas Florath

Je préfère l'approche suivante (code ci-dessous). Il résout le problème de "pollution de l'espace de noms", mais il est également beaucoup plus typé (vous ne pouvez pas affecter 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, Enumérations fortement typées Bjarne Stroustrup (révision 3), juillet 2007
62
Sergey Teplyakov

En général, je mets toujours mes enums dans un struct. J'ai vu plusieurs directives, y compris "préfixer".

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

J'ai toujours pensé que cela ressemblait davantage à C directives qu'à C++ (pour l'une en raison de l'abréviation et également en raison des espaces de noms dans C++).

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

  • espaces de noms
  • structs/classes

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

Les exemples de manipulation incluent:

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

qui retourne le nombre d'éléments d'enum à l'intérieur de la struct T :)

7
Matthieu M.

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

3
Harvey