Masques de bits conformes à la norme C ++ 11 utilisant la classe enum

Pouvez-vous implémenter des masques de bit sécurisés de type conforme à la norme (comme décrit au 17.5.2.1.3 du projet de n3242) à l’aide de la classe enum? Comme je l’ai lu, un type T est un masque de bits s’il prend en charge les opérateurs |, &, ^, ~, | =, & = et ^ = et vous pouvez également le faire si (l & r) où l et r sont de type T L’opérateur! = Et == ne figure pas dans la liste et, pour permettre le sorting, on veut probablement aussi surcharger <.

Amener les opérateurs à travailler n’est que gênant, mais je ne vois pas comment faire si (l & r). Au moins, ce qui suit ne comstack pas avec GCC (outre qu’il est extrêmement dangereux car il permet une conversion implicite erronée en int):

enum class Foo{ operator bool(){ return (unsigned)*this; } }; 

EDIT: Je sais maintenant avec certitude que les classes enum ne peuvent pas avoir de membres. La vraie question de savoir comment faire si (l & r) rest cependant.

Je pense que vous pouvez … Vous devrez append des opérateurs pour les tâches de masquage de bits. Je ne l’ai pas fait ici mais vous pouvez facilement surcharger n’importe quel opérateur relationnel.

  /** * */ // NOTE: I changed to a more descriptive and consistent name // This needs to be a real bitmask type. enum class file_permissions : int { no_perms = 0, owner_read = 0400, owner_write = 0200, owner_exe = 0100, owner_all = 0700, group_read = 040, group_write = 020, group_exe = 010, group_all = 070, others_read = 04, others_write = 02, others_exe = 01, others_all = 07, all_all = owner_all | group_all | others_all, // 0777 set_uid_on_exe = 04000, set_gid_on_exe = 02000, sticky_bit = 01000, perms_mask = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, // 07777 perms_not_known = 0xffff, add_perms = 0x1000, remove_perms = 0x2000, symlink_perms = 0x4000 }; inline constexpr file_permissions operator&(file_permissions x, file_permissions y) { return static_cast (static_cast(x) & static_cast(y)); } inline constexpr file_permissions operator|(file_permissions x, file_permissions y) { return static_cast (static_cast(x) | static_cast(y)); } inline constexpr file_permissions operator^(file_permissions x, file_permissions y) { return static_cast (static_cast(x) ^ static_cast(y)); } inline constexpr file_permissions operator~(file_permissions x) { return static_cast(~static_cast(x)); } inline file_permissions & operator&=(file_permissions & x, file_permissions y) { x = x & y; return x; } inline file_permissions & operator|=(file_permissions & x, file_permissions y) { x = x | y; return x; } inline file_permissions & operator^=(file_permissions & x, file_permissions y) { x = x ^ y; return x; } 

Je ne suis pas tout à fait sûr de vos critères d’acceptation, mais vous pouvez simplement faire en sorte que l’ operator & renvoie une classe wrapper avec les conversions appropriées et un explicit operator bool :

 #include  template using Underlying = typename std::underlying_type::type; template constexpr Underlying underlying(T t) { return Underlying(t); } template struct TruthValue { T t; constexpr TruthValue(T t): t(t) { } constexpr operator T() const { return t; } constexpr explicit operator bool() const { return underlying(t); } }; enum class Color { RED = 0xff0000, GREEN = 0x00ff00, BLUE = 0x0000ff }; constexpr TruthValue operator&(Color l, Color r) { return Color(underlying(l) & underlying(r)); } 

Tous vos autres opérateurs peuvent continuer à renvoyer Color , bien sûr:

 constexpr Color operator|(Color l, Color r) { return Color(underlying(l) | underlying(r)); } constexpr Color operator~(Color c) { return Color(~underlying(c)); } int main() { constexpr Color YELLOW = Color::RED | Color::GREEN; constexpr Color WHITE = Color::RED | Color::GREEN | Color::BLUE; static_assert(YELLOW == (WHITE & ~Color::BLUE), "color subtraction"); return (YELLOW & Color::BLUE) ? 1 : 0; } 

Les énumérations scoped (celles créées avec enum class ou enum struct ) ne sont pas des classes. Ils ne peuvent pas avoir de fonctions membres, ils fournissent simplement des énumérateurs fermés (non visibles au niveau de l’espace de noms).

L’opérateur! = Et == ne figure pas dans la liste.

Ces opérateurs sont déjà supportés par les types d’énumération, les types entiers et std::bitset , il n’est donc pas nécessaire de les surcharger.

et pour permettre le sorting, on veut probablement aussi surcharger <.

Pourquoi voulez-vous sortinger les bitmasks? Est-ce que (a | b) est supérieur à (a | c)? std::ios::in inférieur à std::ios::app ? Est-ce que ça importe? Les opérateurs relationnels sont toujours définis pour les types d’énumération et les types entiers.

Pour répondre à la question principale, vous devez implémenter & tant que fonction non membre surchargée:

 Foo operator&(Foo l, Foo r) { typedef std::underlying_type::type ut; return static_cast(static_cast(l) & static_cast(r)); } 

Je pense que toutes les opérations requirejses pour les types de masque de bits pourraient être définies pour des énumérations étendues, mais pas les exigences telles que

Ci & Cj est non nul et Ci & Cj iszero

et:

La valeur Y est définie dans l’object. X est l’expression. X & Y est différent de zéro.

Étant donné que les domaines énumérés ne prennent pas en charge la conversion implicite en types entiers, vous ne pouvez pas vérifier de manière fiable si elle est différente de zéro ou non. Vous auriez besoin d’écrire if ((X&Y) != bitmask{}) et je ne pense pas que ce soit l’intention du comité.

(Au départ, je pensais qu’ils pouvaient être utilisés pour définir des types de masque de bits, puis je me suis rappelé que j’avais essayé d’en implémenter un en utilisant des énumérations étendues et que j’avais rencontré le problème de test pour zéro / différent de zéro.)

Edit: Je viens de me rappeler que std::launch est un type d’énumération et un type de masque de bits … ainsi, les énumérations apparemment ciblées peuvent être des types de masques!

Un court exemple d’énum-flags ci-dessous.

 #indlude "enum_flags.h" ENUM_FLAGS(foo_t) enum class foo_t { none = 0x00 ,a = 0x01 ,b = 0x02 }; ENUM_FLAGS(foo2_t) enum class foo2_t { none = 0x00 ,d = 0x01 ,e = 0x02 }; int _tmain(int argc, _TCHAR* argv[]) { if(flags(foo_t::a & foo_t::b)) {}; // if(flags(foo2_t::d & foo_t::b)) {}; // Type safety test - won't comstack if uncomment }; 

ENUM_FLAGS (T) est une macro définie dans enum_flags.h (moins de 100 lignes, libre d’utilisation sans ressortingctions).