C ++ enumクラスはメソッドを持つことができますか?


145

2つの値を持つ列挙型クラスがあり、値を受け取ってもう1つの値を返すメソッドを作成したいと思います。型の安全性も維持したいと考えています(そのため、列挙型ではなく列挙型クラスを使用しています)。

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


4
いいえ、できません。こちらをご覧ください
juanchopanza 2014年

@octavian 私の答えに注意して、ユースケースについて考え直してください!
πάνταῥεῖ

@πάνταῥεῖあなたは完全に正しいです、私は列挙型を読みましたが、組合を考え、コメントを殺しました。
Eugen Constantin Dinca 2014年

@octavianがありますあなたも、すべての特定のユースケースを求め、またはあなただけの規格上の制限がしたいんでしたC ++ 11が enum class/struct確認しましたか?
πάνταῥεῖ

私は使用を念頭に置いていました...そしてこれが根本的な問題でした
オクタビアン

回答:


118

いいえ、できません。

私がいることを理解することができますenum classC ++ 11で強く型付けされた列挙型用部品は、あなたがいることを意味するように見えるかもしれないenum持っているclass、あまりにも特徴を、それはそうではないのです。私の学んだ推測では、キーワードの選択は、スコープ付き列挙型を取得するためにC ++ 11の前に使用したパターンに触発されたと思います。

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

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


88
## C ++で、「c ++はできるだけ混乱し、専門家に優しいことを目指していると言われました。明らかにそれは冗談ですが、あなたはアイデアを得ます:)
Stefano Sanfilippo

4
A unionもJohn Doeがクラスと見なすものではありません。それでも、メンバー関数を持つことができます。そして、クラスは実際にはメンバー関数に必須ではありません。以下のような指示使用しvalueたりthis、のようなものenum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; }(ここでも可能に;)、それは機能の他の形態と同じように多くの意味を作ることができます。
セバスチャンマッハ

85

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

私はあなたが次のようなものを書きたいと思うと思います:

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 bool operator==(Fruit a) const { return value == a.value; }
  constexpr bool 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();

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


1
IsYellow()、operator ==、!=もconstexprとしてマークされるべきではありませんか?
Jarek C、

「エラー:トークン「switch」の前にバイナリ演算子がありません」
Pedro77

18

タイトルではなく質問の説明に集中すると、可能な答えは

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

4

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


通常、必要性のための方法持っているenumことがないことを理由から、結果を定期的に(だけインクリメント)列挙型が、値のビットごとの定義の種類をマスクするか、他のビット算術演算を必要とします:

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;

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

struct/ class 仕様は、アクセスのための列挙値のより適切なスコープをサポートします。これ以上、それ以下ではありません!

列挙型(クラス)のメソッドを宣言できない制限を回避する方法はstd::bitset(ラッパークラス)またはビットフィールドを使用することですunionです。

unions、およびそのようなビットフィールド共用体はメソッド持つことできます(制限についてはこちらを参照してください)。

私はサンプルを持っている、(上記のように)それに対応するビットのインデックスにビットマスク値を変換する方法、それを一緒に使用することができますstd::bitset:ここBitIndexConverter.hpp
私はしましたが、ベースのいくつかの「フラグ」decisonの可読性を向上させるため、このかなり便利を見つけましたアルゴリズム。


36
列挙型クラスのメソッドを保証するユースケースは他にもあります(例:toString()やfromString())。すべての(そうでないとしても)現代の主要言語には、C ++ではなく、これ(たとえば、C#、Java、Swift)があります。
マイクリシュケ

1
次回、統一された呼び出し構文を期待しましょう... open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4165.pdf
sdgfsdh

4

あります 、かなり互換性の能力、あなたのコードを書き換えることなくクラスにどの手段列挙型をリファクタリングする(§)効果的にあなたがすることができますあなたはあまり編集せずに行うことを求めていた何が。

(§) ElementWがコメントで指摘しているように、type_traitsに依存するコードは機能しないため、たとえば、autoを使用できません。そしてC ++を破壊することは常に間違いです

enum structおよびenum class仕様についてので、これの一部ではないスコープされています。

元の列挙型は、たとえば「ペット」です(これは単なる例です!)。

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

1
完全な互換性はありません。petテンプレート名、インスタンスauto、またはタイプに関係なく、タイプ名/インスタンスを取得することが予想されるタイプの推論で列挙値を使用する場合decltypepetEnum代わりに、これが壊れます。
ElementW

0

それはあなたのすべてのニーズを満たしていないかもしれませんが、非メンバーのオペレーターであなたはまだ多くの楽しみを持つことができます。例えば:

#include <iostream>

enum class security_level
{
    none, low, medium, high
};

static bool operator!(security_level s) { return s == security_level::none; }

static security_level& operator++(security_level& s)
{
    switch(s)
    {
        case security_level::none: s = security_level::low; break;
        case security_level::low: s = security_level::medium; break;
        case security_level::medium: s = security_level::high; break;
        case security_level::high: break;
    }
    return s;
}

static std::ostream & operator<<(std::ostream &o, security_level s)
{
    switch(s)
    {
        case security_level::none: return o << "none";
        case security_level::low: return o << "low";
        case security_level::medium: return o << "medium";
        case security_level::high: return o << "high";
    }
}

これにより、次のようなコードが可能になります

security_level l = security_level::none;   
if(!!l) { std::cout << "has a security level: " << l << std::endl; } // not reached
++++l;
if(!!l) { std::cout << "has a security level: " << l << std::endl; } // reached: "medium"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.