Comment utiliser les énumérations comme indicateurs dans C ++?

Traiter enum s comme des indicateurs fonctionne bien en C # via l’atsortingbut [Flags] , mais quelle est la meilleure façon de le faire en C ++?

Par exemple, j’aimerais écrire:

 enum AnimalFlags { HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8 }; seahawk.flags = CanFly | EatsFish | Endangered; 

Cependant, j’obtiens des erreurs de compilation concernant les conversions int / enum . Y a-t-il un meilleur moyen d’exprimer cela que de simplement émousser? De préférence, je ne veux pas compter sur les constructions de bibliothèques tierces telles que boost ou Qt.

EDIT: Comme indiqué dans les réponses, je peux éviter l’erreur de compilation en déclarant seahawk.flags comme int . Cependant, je voudrais avoir un mécanisme pour appliquer la sécurité de type, donc quelqu’un ne peut pas écrire seahawk.flags = HasMaximizeButton .

La méthode “correcte” consiste à définir les opérateurs de bits pour l’énumération, comme suit:

 enum AnimalFlags { HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8 }; inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b) {return static_cast(static_cast(a) | static_cast(b));} 

Etc. rest des opérateurs de bit. Modifiez si nécessaire si la plage enum dépasse int range.

Remarque (également un peu hors sujet): Une autre manière de créer des indicateurs uniques peut être effectuée en utilisant un décalage de bits. Je trouve que c’est plus facile à lire.

 enum Flags { A = 1 << 0, // binary 0001 B = 1 << 1, // binary 0010 C = 1 << 2, // binary 0100 D = 1 << 3, // binary 1000 }; 

Il peut contenir des valeurs allant jusqu’à un int, c’est-à-dire que la plupart du temps, 32 indicateurs sont clairement reflétés dans la quantité de décalage.

Pour les paresseux comme moi, voici une solution modélisée pour copier et coller:

 template inline T operator~ (T a) { return (T)~(int)a; } template inline T operator| (T a, T b) { return (T)((int)a | (int)b); } template inline T operator& (T a, T b) { return (T)((int)a & (int)b); } template inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } template inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); } template inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); } template inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); } 

Quel type est la variable seahawk.flags?

En C ++ standard, les énumérations ne sont pas de type sécurisé. Ils sont effectivement des entiers.

AnimalFlags ne doit PAS être le type de votre variable, votre variable devrait être int et l’erreur disparaîtra.

Mettre des valeurs hexadécimales comme d’autres personnes suggérées n’est pas nécessaire, cela ne fait aucune différence.

Les valeurs enum sont de type int par défaut. Ainsi, vous pouvez sûrement les combiner au hasard ou les combiner et les stocker et stocker le résultat dans un int.

Le type enum est un sous-ensemble restreint de int dont la valeur est l’une de ses valeurs énumérées. Par conséquent, lorsque vous créez une nouvelle valeur en dehors de cette plage, vous ne pouvez pas l’assigner sans la convertir en une variable de votre type enum.

Vous pouvez également modifier les types de valeur enum si vous le souhaitez, mais il n’y a aucun point pour cette question.

EDIT: L’affiche a dit qu’ils étaient préoccupés par la sécurité de type et qu’ils ne veulent pas d’une valeur qui ne devrait pas exister dans le type int.

Mais il serait dangereux de placer une valeur en dehors de la plage de AnimalFlags dans une variable de type AnimalFlags.

Il existe un moyen sûr de rechercher des valeurs hors limites dans le type int …

 int iFlags = HasClaws | CanFly; //InvalidAnimalFlagMaxValue-1 gives you a value of all the bits // smaller than itself set to 1 //This check makes sure that no other bits are set. assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0); enum AnimalFlags { HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8, // put new enum values above here InvalidAnimalFlagMaxValue = 16 }; 

Ce qui précède ne vous empêche pas de mettre un drapeau invalide à partir d’un autre enum qui a la valeur 1,2,4 ou 8 bien que.

Si vous voulez une sécurité de type absolue, vous pouvez simplement créer un std :: set et stocker chaque indicateur à l’intérieur. Ce n’est pas un gain de place mais il est de type sécurisé et vous offre la même capacité qu’un bitflag int.

C ++ 0x note: énumérations fortement typées

En C ++ 0x, vous pouvez enfin avoir des valeurs de type enum sûres ….

 enum class AnimalFlags { CanFly = 2, HasClaws = 4 }; if(CanFly == 2) { }//Compiling error 

Notez que si vous travaillez dans un environnement Windows, il existe une macro DEFINE_ENUM_FLAG_OPERATORS définie dans winnt.h qui fait le travail pour vous. Donc, dans ce cas, vous pouvez le faire:

 enum AnimalFlags { HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8 }; DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags) seahawk.flags = CanFly | EatsFish | Endangered; 

Je trouve la réponse actuellement acceptée par eidolon trop dangereuse. L’optimiseur du compilateur peut émettre des hypothèses sur les valeurs possibles dans l’énumération et vous risquez d’obtenir des erreurs avec des valeurs non valides. Et généralement, personne ne veut définir toutes les permutations possibles dans les énumérations de drapeaux.

Comme Brian R. Bondy l’indique ci-dessous, si vous utilisez C ++ 11 (ce que tout le monde devrait faire, c’est très bien), vous pouvez maintenant le faire plus facilement avec enum class :

 enum class ObjectType : uint32_t { ANIMAL = (1 << 0), VEGETABLE = (1 << 1), MINERAL = (1 << 2) }; constexpr enum ObjectType operator |( const enum ObjectType selfValue, const enum ObjectType inValue ) { return (enum ObjectType)(uint32_t(selfValue) | uint32_t(inValue)); } // ... add more operators here. 

Cela garantit une taille et une plage de valeurs stables en spécifiant un type pour l'énumération, empêche la décomposition automatique des énumérations en ints, etc. en utilisant la enum class , et utilise constexpr pour assurer que le code des opérateurs soit intégré et aussi rapide que les nombres normaux. .

Pour les personnes coincées avec des dialectes C ++ antérieurs à 11

Si j'étais coincé avec un compilateur qui ne supporte pas C ++ 11, j'inclurais un type int dans une classe qui n'autorise que l'utilisation des opérateurs binarys et les types de cette énumération pour définir ses valeurs:

 template::type> class SafeEnum { public: SafeEnum() : mFlags(0) {} SafeEnum( ENUM singleFlag ) : mFlags(singleFlag) {} SafeEnum( const SafeEnum& original ) : mFlags(original.mFlags) {} SafeEnum& operator |=( ENUM addValue ) { mFlags |= addValue; return *this; } SafeEnum operator |( ENUM addValue ) { SafeEnum result(*this); result |= addValue; return result; } SafeEnum& operator &=( ENUM maskValue ) { mFlags &= maskValue; return *this; } SafeEnum operator &( ENUM maskValue ) { SafeEnum result(*this); result &= maskValue; return result; } SafeEnum operator ~() { SafeEnum result(*this); result.mFlags = ~result.mFlags; return result; } explicit operator bool() { return mFlags != 0; } protected: UNDERLYING mFlags; }; 

Vous pouvez définir cela comme un enum + typedef régulier:

 enum TFlags_ { EFlagsNone = 0, EFlagOne = (1 << 0), EFlagTwo = (1 << 1), EFlagThree = (1 << 2), EFlagFour = (1 << 3) }; typedef SafeEnum TFlags; 

Et l'utilisation est similaire:

 TFlags myFlags; myFlags |= EFlagTwo; myFlags |= EFlagThree; if( myFlags & EFlagTwo ) std::cout << "flag 2 is set" << std::endl; if( (myFlags & EFlagFour) == EFlagsNone ) std::cout << "flag 4 is not set" << std::endl; 

Et vous pouvez également remplacer le type sous-jacent pour les enum foo : type binarys-stables (comme enum foo : type C ++ 11) en utilisant le deuxième paramètre de modèle, à savoir typedef SafeEnum TFlags; .

J'ai marqué l' operator bool override avec le mot clé explicit de C ++ 11 pour l'empêcher de générer des conversions int, car celles-ci pourraient entraîner la fermeture des ensembles d'indicateurs dans 0 ou 1 lors de leur écriture. Si vous ne pouvez pas utiliser C ++ 11, laissez cette surcharge et réécrivez la première condition dans l'exemple d'utilisation (myFlags & EFlagTwo) == EFlagTwo .

La manière la plus simple de faire cela est illustrée ici , en utilisant le bitet standard de classe de bibliothèque.

Pour émuler la fonctionnalité C # de manière sécurisée, vous devez écrire un template autour du bitet, en remplaçant les arguments int par un enum donné comme paramètre de type au template. Quelque chose comme:

  template  class FlagSet { bitset bits; FlagSet(T enumVal) { bits.set(enumVal); } // etc. }; enum MyFlags { FLAG_ONE, FLAG_TWO }; FlagSet myFlag; 

À mon avis, aucune des réponses obtenues jusqu’à présent n’est idéale. Pour être idéal, je m’attendrais à la solution:

  1. Soutenir le == != , = , & , &= , | , |= et ~ opérateurs au sens conventionnel (ie a & b )
  2. Soyez sûr de ne pas autoriser l’affectation de valeurs non énumérées telles que des littéraux ou des types d’entiers (sauf pour les combinaisons de valeurs énumérées au niveau du bit) ou autoriser l’atsortingbution d’une variable enum à un type entier
  3. Permettre des expressions comme if (a & b)...
  4. Ne nécessite pas de macros maléfiques, de fonctionnalités spécifiques à l’implémentation ou d’autres hacks

Jusqu’à présent, la plupart des solutions se retrouvent aux points 2 ou 3. WebDancer est la solution à mon avis mais échoue au point 3 et doit être répété pour chaque enum.

Ma solution proposée est une version généralisée de WebDancer qui traite également du point 3:

 #include  #include  template::value, T>::type> class auto_bool { T val_; public: constexpr auto_bool(T val) : val_(val) {} constexpr operator T() const { return val_; } constexpr explicit operator bool() const { return static_cast>(val_) != 0; } }; template ::value, T>::type> constexpr auto_bool operator&(T lhs, T rhs) { return static_cast( static_cast::type>(lhs) & static_cast::type>(rhs)); } template ::value, T>::type> constexpr T operator|(T lhs, T rhs) { return static_cast( static_cast::type>(lhs) | static_cast::type>(rhs)); } enum class AnimalFlags : uint8_t { HasClaws = 1, CanFly = 2, EatsFish = 4, Endangered = 8 }; enum class PlantFlags : uint8_t { HasLeaves = 1, HasFlowers = 2, HasFruit = 4, HasThorns = 8 }; int main() { AnimalFlags seahawk = AnimalFlags::CanFly; // Comstacks, as expected AnimalFlags lion = AnimalFlags::HasClaws; // Comstacks, as expected PlantFlags rose = PlantFlags::HasFlowers; // Comstacks, as expected // rose = 1; // Won't comstack, as expected if (seahawk != lion) {} // Comstacks, as expected // if (seahawk == rose) {} // Won't comstack, as expected // seahawk = PlantFlags::HasThorns; // Won't comstack, as expected seahawk = seahawk | AnimalFlags::EatsFish; // Comstacks, as expected lion = AnimalFlags::HasClaws | // Comstacks, as expected AnimalFlags::Endangered; // int eagle = AnimalFlags::CanFly | // Won't comstack, as expected // AnimalFlags::HasClaws; // int has_claws = seahawk & AnimalFlags::CanFly; // Won't comstack, as expected if (seahawk & AnimalFlags::CanFly) {} // Comstacks, as expected seahawk = seahawk & AnimalFlags::CanFly; // Comstacks, as expected return 0; } 

Cela crée des surcharges des opérateurs nécessaires mais utilise SFINAE pour les limiter aux types énumérés. Notez que par souci de concision, je n’ai pas défini tous les opérateurs, mais le seul qui soit différent est le & . Les opérateurs sont actuellement globaux (c’est-à-dire qu’ils s’appliquent à tous les types énumérés) mais cela pourrait être réduit en plaçant les surcharges dans un espace de noms (ce que je fais) ou en ajoutant des conditions SFINAE supplémentaires (peut-être en utilisant des types sous-jacents particuliers) ). Le underlying_type_t est une fonctionnalité C ++ 14 mais il semble être bien supporté et facile à émuler pour C ++ 11 avec un simple template using underlying_type_t = underlying_type::type;

Si votre compilateur ne supporte pas encore les énumérations fortement typées, vous pouvez consulter l’ article suivant de la source c ++:

De l’abstrait:

Cet article présente une solution au problème de la contrainte des opérations sur les bits pour
n’autorisez que celles sûres et légitimes, et transformez toutes les manipulations de bits non valides en erreurs de compilation. Mieux encore, la syntaxe des opérations sur les bits rest inchangée et le code fonctionnant avec les bits n’a pas besoin d’être modifié, sauf peut-être pour corriger les erreurs qui n’avaient pas encore été détectées.

Je me suis retrouvé à poser la même question et à proposer une solution générique basée sur C ++ 11, similaire à celle de soru:

 template  class FlagSet { private: using TUNDER = typename std::underlying_type::type; std::bitset::max()> m_flags; public: FlagSet() = default; template  FlagSet(TENUM f, ARGS... args) : FlagSet(args...) { set(f); } FlagSet& set(TENUM f) { m_flags.set(static_cast(f)); return *this; } bool test(TENUM f) { return m_flags.test(static_cast(f)); } FlagSet& operator|=(TENUM f) { return set(f); } }; 

L’interface peut être améliorée au goût. Ensuite, il peut être utilisé comme ça:

 FlagSet flags{Flags::FLAG_A, Flags::FLAG_C}; flags |= Flags::FLAG_D; 

Le standard C ++ en parle explicitement, voir section “17.5.2.1.3 Types de masque”:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf

Compte tenu de ce “modèle”, vous obtenez:

 enum AnimalFlags : unsigned int { HasClaws = 1, CanFly = 2, EatsFish = 4, Endangered = 8 }; constexpr AnimalFlags operator|(AnimalFlags X, AnimalFlags Y) { return static_cast( static_cast(X) | static_cast(Y)); } AnimalFlags& operator|=(AnimalFlags& X, AnimalFlags Y) { X = X | Y; return X; } 

Et similaire pour les autres opérateurs. Notez également le “constexpr”, il est nécessaire si vous voulez que le compilateur puisse exécuter le temps de compilation des opérateurs.

Si vous utilisez C ++ / CLI et que vous souhaitez pouvoir affecter des membres enum des classes ref, vous devez utiliser des références de suivi à la place:

 AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) { X = X | Y; return X; } 

NOTE: Cet exemple n’est pas complet, voir section “17.5.2.1.3 Types de masque de bits” pour un ensemble complet d’opérateurs.

Vous confondez les objects et les collections d’objects. Plus précisément, vous confondez les indicateurs binarys avec des ensembles d’indicateurs binarys. Une solution appropriée ressemblerait à ceci:

 // These are individual flags enum AnimalFlag // Flag, not Flags { HasClaws = 0, CanFly, EatsFish, Endangered }; class AnimalFlagSet { int m_Flags; public: AnimalFlagSet() : m_Flags(0) { } void Set( AnimalFlag flag ) { m_Flags |= (1 << flag); } void Clear( AnimalFlag flag ) { m_Flags &= ~ (1 << flag); } bool Get( AnimalFlag flag ) const { return (m_Flags >> flag) & 1; } }; 

Voici une option pour les masques de bits si vous ne les utilisez pas pour les valeurs d’énumération individuelles (par exemple, vous n’avez pas besoin de les désactiver) … et si vous n’êtes pas préoccupé par le maintien de la compatibilité binary, par exemple: ne vous souciez pas de savoir où vivent vos bits … ce que vous êtes probablement. De plus, il ne faut pas trop se préoccuper de la scope et du contrôle d’access. Hummm, les énumérations ont de belles propriétés pour les champs de bits … me demande si quelqu’un a déjà essayé ça 🙂

 struct AnimalProperties { bool HasClaws : 1; bool CanFly : 1; bool EatsFish : 1; bool Endangered : 1; }; union AnimalDescription { AnimalProperties Properties; int Flags; }; void TestUnionFlags() { AnimalDescription propertiesA; propertiesA.Properties.CanFly = true; AnimalDescription propertiesB = propertiesA; propertiesB.Properties.EatsFish = true; if( propertiesA.Flags == propertiesB.Flags ) { cout << "Life is terrible :("; } else { cout << "Life is great!"; } AnimalDescription propertiesC = propertiesA; if( propertiesA.Flags == propertiesC.Flags ) { cout << "Life is great!"; } else { cout << "Life is terrible :("; } } 

Nous pouvons voir que la vie est belle, nous avons nos valeurs discrètes et nous avons un bon int & & | à notre coeur le contenu, qui a encore le contexte de ce que ses bits signifient. Tout est cohérent et prévisible ... pour moi ... aussi longtemps que je continue à utiliser le compilateur VC ++ de Microsoft avec la mise à jour 3 sur Win10 x64 et que je ne touche pas les indicateurs du compilateur 🙂

Même si tout va bien ... nous avons un certain contexte quant à la signification des drapeaux maintenant, car dans une union avec le champ de bits dans le monde réel terrible où votre programme peut être responsable de plus d'une tâche discrète, vous pouvez toujours accidentellement (assez facilement) casser deux champs de drapeaux de différents syndicats (disons, AnimalProperties et ObjectProperties, car ils sont tous les deux ints), mélangeant tous vos bits, ce qui est un horrible bug pour tracer ... et comment je sais Beaucoup de gens sur ce post ne travaillent pas très souvent avec des masques, car leur construction est facile et leur maintenance est difficile.

 class AnimalDefinition { public: static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags ); //A little too obvious for my taste... NEXT! static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties ); //Oh I see how to use this! BORING, NEXT! static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something? AnimalFlags animalFlags; //Well this is *way* too hard to break unintentionally, screw this! int flags; //PERFECT! Nothing will ever go wrong here... //wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation? //Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew! private: AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :( } 

Donc, vous rendez votre déclaration syndicale privée pour empêcher l'access direct à "Flags", et vous devez append des getters / setters et des surcharges d'opérateurs, puis créer une macro pour tout cela, et Faites ceci avec un Enum.

Malheureusement, si vous voulez que votre code soit portable, je ne pense pas qu'il y ait de moyen de garantir la disposition des bits ou B) de déterminer la disposition des bits au moment de la compilation (afin de versions / plates-formes, etc.) Offset dans une structure avec des champs de bits

Au moment de l'exécution, vous pouvez jouer des astuces avec la définition des champs et XOR sur les indicateurs pour voir quels bits ont changé, ce qui me semble bien minable, même s'il s'agit d'une solution 100% cohérente, indépendante de la plateforme et totalement déterministe.

TL; DR: N'écoutez pas les ennemis. C ++ n'est pas l'anglais. Le fait que la définition littérale d'un mot-clé abrégé hérité de C ne corresponde pas à votre utilisation ne signifie pas que vous ne devriez pas l'utiliser lorsque la définition C et C ++ du mot-clé inclut absolument votre cas d'utilisation. Vous pouvez également utiliser des structures pour modéliser des choses autres que des structures, et des classes pour des choses autres que la caste scolaire et sociale. Vous pouvez utiliser float pour les valeurs qui sont mises à la terre. Vous pouvez utiliser char pour les variables qui ne sont ni brûlées ni une personne dans un roman, une pièce de théâtre ou un film. Tout programmeur qui va au dictionnaire pour déterminer la signification d'un mot-clé avant que la spécification de langue ne soit un ... eh bien je tiendrai ma langue là-bas.

Si vous voulez que votre code soit calqué sur la langue parlée, vous feriez mieux d'écrire en Objective-C, qui, incidemment, utilise également énormément les énumérations pour les champs de bits.

Je voudrais développer la réponse à Uliwitness , en corrigeant son code pour C ++ 98 et en utilisant l’ idiome Safe Bool , en l’absence du modèle std::underlying_type<> et du mot-clé explicit dans les versions C ++ au-dessous de C ++ 11.

Je l’ai également modifié pour que les valeurs enum puissent être séquentielles sans aucune affectation explicite, vous pouvez donc avoir

 enum AnimalFlags_ { HasClaws, CanFly, EatsFish, Endangered }; typedef FlagsEnum AnimalFlags; seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered; 

Vous pouvez alors obtenir la valeur des indicateurs bruts avec

 seahawk.flags.value(); 

Voici le code.

 template  class FlagsEnum { typedef Underlying FlagsEnum::* RessortingctedBool; public: FlagsEnum() : m_flags(Underlying()) {} FlagsEnum(EnumType singleFlag): m_flags(1 << singleFlag) {} FlagsEnum(const FlagsEnum& original): m_flags(original.m_flags) {} FlagsEnum& operator |=(const FlagsEnum& f) { m_flags |= f.m_flags; return *this; } FlagsEnum& operator &=(const FlagsEnum& f) { m_flags &= f.m_flags; return *this; } friend FlagsEnum operator |(const FlagsEnum& f1, const FlagsEnum& f2) { return FlagsEnum(f1) |= f2; } friend FlagsEnum operator &(const FlagsEnum& f1, const FlagsEnum& f2) { return FlagsEnum(f1) &= f2; } FlagsEnum operator ~() const { FlagsEnum result(*this); result.m_flags = ~result.m_flags; return result; } operator RestrictedBool() const { return m_flags ? &FlagsEnum::m_flags : 0; } Underlying value() const { return m_flags; } protected: Underlying m_flags; }; 

Comme ci-dessus (Kai) ou procédez comme suit. Vraiment les énumérations sont des “énumérations”, ce que vous voulez faire est d’avoir un ensemble, donc vous devriez vraiment utiliser stl :: set

 enum AnimalFlags { HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8 }; int main(void) { AnimalFlags seahawk; //seahawk= CanFly | EatsFish | Endangered; seahawk= static_cast(CanFly | EatsFish | Endangered); } 

Voici ma solution sans avoir besoin de surcharger ou de lancer:

 namespace EFoobar { enum { FB_A = 0x1, FB_B = 0x2, FB_C = 0x4, }; typedef long Flags; } void Foobar(EFoobar::Flags flags) { if (flags & EFoobar::FB_A) // do sth ; if (flags & EFoobar::FB_B) // do sth ; } void ExampleUsage() { Foobar(EFoobar::FB_A | EFoobar::FB_B); EFoobar::Flags otherflags = 0; otherflags|= EFoobar::FB_B; otherflags&= ~EFoobar::FB_B; Foobar(otherflags); } 

Je pense que ça va, parce que nous identifions les énumérations et les ints (pas fortement typés) de toute façon.

Juste comme une note latérale (plus longue), si vous

  • vouloir utiliser des énumérations fortement typées et
  • Je n’ai pas besoin de trop jouer avec vos drapeaux
  • la performance n’est pas un problème

Je voudrais venir avec ceci:

 #include  enum class EFoobarFlags { FB_A = 1, FB_B, FB_C, }; void Foobar(const std::set& flags) { if (flags.find(EFoobarFlags::FB_A) != flags.end()) // do sth ; if (flags.find(EFoobarFlags::FB_B) != flags.end()) // do sth ; } void ExampleUsage() { Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B}); std::set otherflags{}; otherflags.insert(EFoobarFlags::FB_B); otherflags.erase(EFoobarFlags::FB_B); Foobar(otherflags); } 

en utilisant les listes d’initialisation C ++ 11 et la enum class .

Peut-être comme NS_OPTIONS d’Objective-C.

 #define ENUM(T1, T2) \ enum class T1 : T2; \ inline T1 operator~ (T1 a) { return (T1)~(int)a; } \ inline T1 operator| (T1 a, T1 b) { return static_cast((static_cast(a) | static_cast(b))); } \ inline T1 operator& (T1 a, T1 b) { return static_cast((static_cast(a) & static_cast(b))); } \ inline T1 operator^ (T1 a, T1 b) { return static_cast((static_cast(a) ^ static_cast(b))); } \ inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast((reinterpret_cast(a) |= static_cast(b))); } \ inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast((reinterpret_cast(a) &= static_cast(b))); } \ inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast((reinterpret_cast(a) ^= static_cast(b))); } \ enum class T1 : T2 ENUM(Options, short) { FIRST = 1 << 0, SECOND = 1 << 1, THIRD = 1 << 2, FOURTH = 1 << 3 }; auto options = Options::FIRST | Options::SECOND; options |= Options::THIRD; if ((options & Options::SECOND) == Options::SECOND) cout << "Contains second option." << endl; if ((options & Options::THIRD) == Options::THIRD) cout << "Contains third option." << endl; return 0; // Output: // Contains second option. // Contains third option. 

Only syntactic sugar. No additional metadata.

 namespace UserRole // grupy { constexpr uint8_t dea = 1; constexpr uint8_t red = 2; constexpr uint8_t stu = 4; constexpr uint8_t kie = 8; constexpr uint8_t adm = 16; constexpr uint8_t mas = 32; } 

Flag operators on integral type just works.