web-dev-qa-db-ja.com

C ++ enumクラスにメソッドを含めることはできますか?

2つの値を持つ列挙クラスがあり、値を受け取ってもう1つを返すメソッドを作成したいと思います。型の安全性も維持したい(だから列挙型の代わりに列挙型クラスを使用する)。

http://www.cplusplus.com/doc/tutorial/other_data_types/ メソッドについては何も言及していませんが、どのタイプのクラスもメソッドを持つことができるという印象を受けました。

108
octavian

いいえ、できません。

C++ 11の強く型付けされた列挙型のenum class部分は、あなたのenumclass特性も持っていることを暗示しているように見えるかもしれませんが、そうではありません。私の知識によると、キーワードの選択は、スコープ付き列挙型を取得するためにC++ 11より前に使用したパターンに触発されたものです。

class Foo {
public:
  enum {BAR, BAZ};
};

ただし、それは単なる構文です。繰り返しますが、enum classclassではありません。

95

「できない」という答えは技術的には正しいですが、次のアイデアを使用して、探している動作を実現できる可能性があると思います。

次のようなものを書きたいと思います。

Fruit f = Fruit::Strawberry;
f.IsYellow();

そして、コードが次のようになることを望んでいました。

enum class Fruit : uint8_t
{
  Apple, 
  Pear,
  Banana,
  Strawberry,

  bool IsYellow() { return this == Banana; }
};

...

しかし、もちろん、列挙型はメソッドを持つことができないため、機能しません(上記のコンテキストでは「これ」は何も意味しません)

ただし、非クラスの列挙型を含む通常のクラスと、その型の値を含む単一のメンバー変数の概念を使用すると、必要な構文/動作/型の安全性に非常に近づくことができます。すなわち:

class Fruit
{
public:
  enum Value : uint8_t
  {
    Apple,
    Pear,
    Banana,
    Strawberry
  };

  Fruit() = default;
  constexpr Fruit(Value aFruit) : value(aFruit) { }

#if Enable switch(fruit) use case:
  operator Value() const { return value; }  // Allow switch and comparisons.
                                            // note: Putting constexpr here causes
                                            // clang to stop warning on incomplete
                                            // case handling.
  explicit operator bool() = delete;        // Prevent usage: if(fruit)
#else
  constexpr operator==(Fruit a) const { return value == a.value; }
  constexpr operator!=(Fruit a) const { return value != a.value; }
#endif

  constexpr bool IsYellow() const { return value == Banana; }

private:
  Value value;
};

今、あなたは書くことができます:

Fruit f = Fruit::Strawberry;
f.IsYellow();

そして、コンパイラは次のようなことを防ぎます:

Fruit f = 1;  // Compile time error.

次のようなメソッドを簡単に追加できます。

Fruit f("Apple");

そして

f.ToString();

サポートすることができます。

41
jtlim

タイトルの代わりに質問の説明に集中すると、考えられる答えは

struct LowLevelMouseEvent {
    enum Enum {
        mouse_event_uninitialized = -2000000000, // generate crash if try to use it uninitialized.
        mouse_event_unknown = 0,
        mouse_event_unimplemented,
        mouse_event_unnecessary,
        mouse_event_move,
        mouse_event_left_down,
        mouse_event_left_up,
        mouse_event_right_down,
        mouse_event_right_up,
        mouse_event_middle_down,
        mouse_event_middle_up,
        mouse_event_wheel
    };
    static const char* ToStr (const type::LowLevelMouseEvent::Enum& event)
    {
        switch (event) {
            case mouse_event_unknown:         return "unknown";
            case mouse_event_unimplemented:   return "unimplemented";
            case mouse_event_unnecessary:     return "unnecessary";
            case mouse_event_move:            return "move";
            case mouse_event_left_down:       return "left down";
            case mouse_event_left_up:         return "left up";
            case mouse_event_right_down:      return "right down";
            case mouse_event_right_up:        return "right up";
            case mouse_event_middle_down:     return "middle down";
            case mouse_event_middle_up:       return "middle up";
            case mouse_event_wheel:           return "wheel";
            default:
                Assert (false);
                break;
        }
        return "";
    }
};
16
Márkus Attila

その他の回答 で述べたように、いいえ。 enum classでさえクラスではありません。


通常、 need enumのメソッドを持っているのは、それがregular(単なる増分)列挙ではなく、マスクされる値のビットごとの定義、または他のビット算術演算が必要です。

enum class Flags : unsigned char {
    Flag1 = 0x01 , // Bit #0
    Flag2 = 0x02 , // Bit #1
    Flag3 = 0x04 , // Bit #3
    // aso ...
}

// Sets both lower bits
unsigned char flags = (unsigned char)(Flags::Flag1 | Flags::Flag2);

// Set Flag3
flags |= Flags::Flag3;

// Reset Flag2
flags &= ~Flags::Flag2;

明らかに、必要な操作をカプセル化して、ビットの単一/グループを再設定/設定することを考えます。このような「フラグ」セットの操作には、ビットマスク値またはビットインデックス駆動型の操作も役立ちます。

c ++ 11struct/classspecification は、アクセス用の列挙値のより良いスコープをサポートするだけです。これ以上でもそれ以下でもありません!

制限から抜け出す方法は、 enum(クラス)のメソッドを宣言できません。 std::bitset (ラッパークラス)または- ビットフィールドunion

unions、およびそのようなビットフィールド共用体 can にはメソッドがあります(制限については here を参照してください!)。

サンプルがあります。ビットマスク値(上記を参照)を対応するビットインデックスに変換する方法です。ここでstd::bitsetと共に使用できます。 BitIndexConverter.hpp
これは、いくつかの「フラグ」決定ベースのアルゴリズムの可読性を高めるのに非常に役立つことがわかりました。

4

かなり互換性のある機能(§)があり、コードを書き直すことなくenumをクラスにリファクタリングします。つまり、効果的にできるやりたいことをしますあまり編集せずに。

(§)ElementWがコメントで指摘しているように、type_traits依存コードは機能しません。 autoなどを使用することはできません。このような処理方法はあるかもしれませんが、最終的にはenumをクラスに変換しているため、C++を破壊するのは常に間違いです。

enum structおよびenum classの仕様はスコープに関するものなので、この一部ではありません。

元の列挙型は、たとえば'pet'(これは単なる例です!)。

enum pet { 
    fish, cat, dog, bird, rabbit, other 
};

(1)例えば既存のコードから隠すために、petEnumに変更します。

enum petEnum { 
    fish, cat, dog, bird, rabbit, other 
};

(2)その下に新しいクラス宣言を追加します(元の列挙で名前が付けられます)

class pet {
    private:
        petEnum value;
        pet() {}

    public:
        pet(const petEnum& v) : value{v} {} //not explicit here.
        operator petEnum() const { return value; }
        pet& operator=(petEnum v) { value = v; return *this;}
        bool operator==(const petEnum v) const { return value == v; }
        bool operator!=(const petEnum v) const { return value != v; }
 //     operator std::string() const;

};

(3)好きなクラスメソッドをペットクラスに追加できるようになりました。例えば。文字列演算子

    pet::operator std::string() const {
        switch (value) {
            case fish: return "fish";
            case cat:  return "cat";
            case dog:  return "dog";
            case bird: return "bird";
            case rabbit: return "rabbit";
            case other: return "Wow. How exotic of you!";
        }
    }

これで、たとえばstd :: cout ...を使用できます。

int main() {
    pet myPet = rabbit;
    if(myPet != fish) {
        cout << "No splashing! ";
    }
    std::cout << "I have a " << std::string(myPet) << std::endl;
    return 0;
}
2
Konchog