2つの値を持つ列挙型クラスがあり、値を受け取ってもう1つの値を返すメソッドを作成したいと思います。型の安全性も維持したいと考えています(そのため、列挙型ではなく列挙型クラスを使用しています)。
http://www.cplusplus.com/doc/tutorial/other_data_types/はメソッドについて何も触れていませんが、どのタイプのクラスにもメソッドを含めることができるという印象を受けました。
2つの値を持つ列挙型クラスがあり、値を受け取ってもう1つの値を返すメソッドを作成したいと思います。型の安全性も維持したいと考えています(そのため、列挙型ではなく列挙型クラスを使用しています)。
http://www.cplusplus.com/doc/tutorial/other_data_types/はメソッドについて何も触れていませんが、どのタイプのクラスにもメソッドを含めることができるという印象を受けました。
回答:
いいえ、できません。
私がいることを理解することができますenum class
C ++ 11で強く型付けされた列挙型用部品は、あなたがいることを意味するように見えるかもしれないenum
持っているclass
、あまりにも特徴を、それはそうではないのです。私の学んだ推測では、キーワードの選択は、スコープ付き列挙型を取得するためにC ++ 11の前に使用したパターンに触発されたと思います。
class Foo {
public:
enum {BAR, BAZ};
};
ただし、これは単なる構文です。繰り返しますが、でenum class
はありませんclass
。
union
もJohn Doeがクラスと見なすものではありません。それでも、メンバー関数を持つことができます。そして、クラスは実際にはメンバー関数に必須ではありません。以下のような指示使用しvalue
たりthis
、のようなものenum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; }
(ここでも可能に;
)、それは機能の他の形態と同じように多くの意味を作ることができます。
「できない」という答えは技術的に正しいですが、次のアイデアを使用して、探している動作を実現できる可能性があります。
私はあなたが次のようなものを書きたいと思うと思います:
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();
サポートすることができます。
タイトルではなく質問の説明に集中すると、可能な答えは
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 "";
}
};
他の回答で述べたように、いいえ。でも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;
明らかに、たとえばビットマスク値またはビットインデックス駆動の操作によって、ビットの単一/グループを再設定/設定するために必要な操作をカプセル化することを考えると、このような「フラグ」のセットの操作に役立ちます。
の c ++ 11 struct
/ class
仕様は、アクセスのための列挙値のより適切なスコープをサポートします。これ以上、それ以下ではありません!
列挙型(クラス)のメソッドを宣言できない制限を回避する方法は、std::bitset
(ラッパークラス)またはビットフィールドを使用することですunion
です。
union
s、およびそのようなビットフィールド共用体はメソッドを持つことができます(制限についてはこちらを参照してください)。
私はサンプルを持っている、(上記のように)それに対応するビットのインデックスにビットマスク値を変換する方法、それを一緒に使用することができますstd::bitset
:ここBitIndexConverter.hpp
私はしましたが、ベースのいくつかの「フラグ」decisonの可読性を向上させるため、このかなり便利を見つけましたアルゴリズム。
あります 、かなり互換性の能力、あなたのコードを書き換えることなくクラスにどの手段列挙型をリファクタリングする(§)効果的にあなたがすることができますあなたはあまり編集せずに行うことを求めていた何が。
(§) 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;
}
pet
テンプレート名、インスタンスauto
、またはタイプに関係なく、タイプ名/インスタンスを取得することが予想されるタイプの推論で列挙値を使用する場合decltype
、petEnum
代わりに、これが壊れます。
それはあなたのすべてのニーズを満たしていないかもしれませんが、非メンバーのオペレーターであなたはまだ多くの楽しみを持つことができます。例えば:
#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"