36

列挙型クラスを使用して、(n3242 ドラフトの 17.5.2.1.3 で説明されているように) 標準準拠のタイプ セーフなビットマスクを実装できますか? 私が読んだ方法では、タイプ T は、|、&、^、~、|=、&=、および ^= 演算子をサポートしている場合はビットマスクであり、さらに if(l&r) を実行できます。ここで、l と r は T 型です。 . リストにないのは演算子 != と == であり、並べ替えを可能にするために、おそらく < もオーバーロードする必要があります。

演算子を機能させるのは煩わしいボイラープレート コードですが、if(l&r) の実行方法がわかりません。少なくとも以下は GCC でコンパイルされません (int への誤った暗黙的な変換を許してしまうため、非常に危険です):

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

編集:列挙型クラスがメンバーを持つことができないことは確かです。if(l&r) をどのように行うかという実際の問題は残っています。

4

5 に答える 5

34

できると思います...ビットマスクの演算子を追加する必要があります。ここではしませんでしたが、関係演算子を簡単にオーバーロードできます。

  /**
   *
   */
  // 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;
  }
于 2012-08-22T20:01:44.433 に答える
11

あなたの受け入れ基準が何であるかは完全にはわかりませんが、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;
}
于 2012-08-21T21:31:39.487 に答える
4

範囲指定された列挙 (enum classまたはで作成されたものenum struct) はクラスではありません。メンバー関数を持つことはできません。囲まれた列挙子を提供するだけです (名前空間レベルでは表示されません)。

于 2012-08-21T17:39:13.123 に答える
3

以下の列挙型フラグの短い例。

#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 行未満、制限なしで自由に使用できます)。

于 2013-07-21T10:28:34.170 に答える
3

リストにないのは演算子 != と == です

これらの演算子は、列挙型、整数型で既にサポートされているstd::bitsetため、それらをオーバーロードする必要はありません。

また、並べ替えを可能にするために、< をオーバーロードすることもできます。

なぜビットマスクをソートしたいのですか? (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 & Cjはゼロではなく、Ci & Cj はゼロです

と:

Y はオブジェクトXに設定され、式X & Yはゼロではありません。

スコープ付き列挙型は整数型への暗黙的な変換をサポートしていないため、非ゼロかどうかを確実にテストすることはできません。あなたは書く必要がありますがif ((X&Y) != bitmask{})、それは委員会の意図ではないと思います.

(最初はビットマスク型を定義するために使用できると思っていましたが、スコープ付き列挙型を使用して実装しようとしたことを思い出し、ゼロ/非ゼロのテストで問題が発生しました。)

編集:スコープ付き列挙型とビットマスク型であることを思い出しましたstd::launch...明らかにスコープ付き列挙型はビットマスク型にすることができます!

于 2012-08-21T20:48:11.837 に答える