使用枚举类的C ++ 11标准符合位掩码

时间:2012-08-21 17:09:04

标签: c++ c++11 bit-fields bitmask enum-class

您是否可以使用枚举类实现标准符合(如n3242草案的17.5.2.1.3所述)类型安全位掩码?我读它的方式,类型T是一个位掩码,如果它支持|,&,^,〜,| =,& =和^ =运算符,你还可以做if(l& r)其中l和r类型为T.缺少列表中的运算符!=和==并允许排序一个可能也想要重载<。

让操作员工作只是烦人的样板代码,但我不知道如何(l& r)。至少以下不能用GCC编译(除了极其危险,因为它允许错误的隐式转换为int):

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

编辑:我现在肯定知道枚举类不能有成员。实际问题如果(l& r)仍然存在。

5 个答案:

答案 0 :(得分:24)

我想你可以...... 你必须为bitmasky事物添加运算符。我没有在这里做,但你可以轻松地重载任何关系运算符。

  /**
   *
   */
  // 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<file_permissions>
      (static_cast<int>(x) & static_cast<int>(y));
  }

  inline constexpr file_permissions
  operator|(file_permissions x, file_permissions y)
  {
    return static_cast<file_permissions>
      (static_cast<int>(x) | static_cast<int>(y));
  }

  inline constexpr file_permissions
  operator^(file_permissions x, file_permissions y)
  {
    return static_cast<file_permissions>
      (static_cast<int>(x) ^ static_cast<int>(y));
  }

  inline constexpr file_permissions
  operator~(file_permissions x)
  {
    return static_cast<file_permissions>(~static_cast<int>(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;
  }

答案 1 :(得分:9)

我不完全确定您的接受标准是什么,但您可以让operator &返回包含适当转化和explicit operator bool的包装类:

#include <type_traits>

template<typename T> using Underlying = typename std::underlying_type<T>::type;
template<typename T> constexpr Underlying<T>
underlying(T t) { return Underlying<T>(t); }

template<typename T> 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<Color>
operator&(Color l, Color r) { return Color(underlying(l) & underlying(r)); }

所有其他运营商当然可以继续返回Color

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;
}

答案 2 :(得分:4)

范围内的枚举(使用enum classenum struct创建的枚举)不是类。它们不能具有成员函数,它们只提供封闭的枚举器(在命名空间级别不可见)。

答案 3 :(得分:3)

  

列表中缺少的是运算符!=和==

枚举类型,整数类型和std::bitset已经支持这些运算符,因此不需要重载它们。

  

并允许排序可能还想重载&lt;。

为什么要对位掩码进行排序? (a | b)是否大于(a | c)? std::ios::in是否小于std::ios::app?有关系吗?始终为枚举类型和整数类型定义关系运算符。

要回答主要问题,您应将&实施为重载的非会员功能:

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

我相信可以为作用域枚举定义所有必需的位掩码类型操作,但不能为

等要求定义
  

Ci&amp; Cj 非零且 Ci&amp; Cj iszero

  

Y在对象中设置 X 是表达式 X&amp; Y 非零。

由于作用域枚举不支持将整个类型的impicit转换,因此无法可靠地测试它是否为非零。你需要写if ((X&Y) != bitmask{})而我认为这不是委员会的意图。

(我最初认为它们可以用于定义位掩码类型,然后记得我曾尝试使用作用域枚举实现一个并遇到测试零/非零的问题。)

编辑:我刚才记得std::launch是一个范围内的枚举类型和一个位掩码类型......所以显然范围的枚举可以是位掩码类型!

答案 4 :(得分:2)

下面的枚举标记的简短示例。

#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 compile if uncomment
    };

ENUM_FLAGS(T)是一个宏,在enum_flags.h中定义(少于100行,可以免费使用,无限制)。

相关问题