Un enum X : int
(C #) ou enum class X : int
(C ++ 11) est un type qui a un champ interne caché int
pouvant contenir n'importe quelle valeur. De plus, un nombre de constantes prédéfinies de X
sont définies sur l'énum. Il est possible de convertir l'énum en son entier et inversement. Ceci est vrai à la fois en C # et en C ++ 11.
En C #, les énumérations ne sont pas seulement utilisées pour conserver des valeurs individuelles, mais également pour contenir des combinaisons d'indicateurs au niveau du bit, conformément à la recommandation de Microsoft . Ces énumérations sont (généralement, mais pas nécessairement) décorées avec l' [Flags]
attribut. Pour faciliter la vie des développeurs, les opérateurs au niveau des bits (OR, AND, etc ...) sont surchargés afin que vous puissiez facilement faire quelque chose comme ça (C #):
void M(NumericType flags);
M(NumericType.Sign | NumericType.ZeroPadding);
Je suis un développeur C # expérimenté, mais je ne programme C ++ que depuis quelques jours maintenant et je ne suis pas connu des conventions C ++. J'ai l'intention d'utiliser un enum C ++ 11 exactement de la même manière que j'avais l'habitude de le faire en C #. En C ++ 11, les opérateurs de bits sur les énumérations étendues ne sont pas surchargés. Je voulais donc les surcharger .
Cela a suscité un débat et les opinions semblent varier entre trois options:
Une variable du type enum est utilisée pour contenir le champ de bits, similaire à C #:
void M(NumericType flags); // With operator overloading: M(NumericType::Sign | NumericType::ZeroPadding); // Without operator overloading: M(static_cast<NumericType>(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding)));
Mais cela irait à l’encontre de la philosophie d’énumération fortement typée des énumérations étendues de C ++ 11.
Utilisez un entier simple si vous souhaitez stocker une combinaison d'énums au niveau du bit:
void M(int flags); M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));
Mais cela réduirait tout à un
int
, ne vous laissant aucune idée du type que vous êtes censé mettre dans la méthode.Ecrivez une classe séparée qui surchargera les opérateurs et maintiendra les indicateurs au niveau du bit dans un champ entier caché:
class NumericTypeFlags { unsigned flags_; public: NumericTypeFlags () : flags_(0) {} NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {} //...define BITWISE test/set operations }; void M(NumericTypeFlags flags); M(NumericType::Sign | NumericType::ZeroPadding);
( code complet par utilisateur315052 )
Mais alors vous n’avez pas d’IntelliSense ni aucun autre support pour vous indiquer les valeurs possibles.
Je sais que c’est une question subjective , mais: quelle approche dois-je utiliser? Quelle approche, le cas échéant, est la plus largement reconnue en C ++? Quelle approche utilisez-vous pour traiter les champs de bits et pourquoi ?
Bien sûr, étant donné que les trois approches fonctionnent, je cherche des raisons factuelles et techniques, des conventions généralement acceptées, et pas simplement des préférences personnelles.
Par exemple, à cause de mon arrière-plan C #, j'ai tendance à choisir l'approche 1 en C ++. Cela présente l'avantage supplémentaire que mon environnement de développement peut m'indiquer les valeurs possibles. Avec les opérateurs d'enum surchargés, il est facile à écrire et à comprendre, et plutôt propre. Et la signature de la méthode indique clairement le type de valeur qu’elle attend. Mais la plupart des gens ici ne sont pas d'accord avec moi, probablement pour une bonne raison.
enum E { A = 1, B = 2, C = 4, };
, la plage est 0..7
(3 bits). Ainsi, la norme C ++ garantit explicitement que # 1 sera toujours une option viable. [Spécifiquement, sauf si spécifié autrement , par enum class
défaut enum class : int
, et a donc toujours un type sous-jacent fixe.])