27

enum がどのように機能するかを理解するために、C++ 標準を少し読んでみました。当初考えていたよりも実際にはたくさんあります。

スコープ付き列挙のint場合、enum-base 句で特に指定しない限り、基になる型が であることは明らかです (任意の整数型にすることができます)。

enum class color { red, green, blue};  // these are int

スコープのない列挙の場合、基になる型は機能する任意の整数型にすることができ、必要な場合を除き、int よりも大きくならないようです。

enum color { red, green, blue};  // underlying type may vary

基になるスコープのない列挙型は標準化されていないため、インスタンスのシリアル化を処理する最良の方法は何ですか? これまでのところ、int書き込み時に に変換してから にシリアル化し、読み取り時に変数をスイッチにint設定してenumいましたが、少しぎこちないようです。より良い方法はありますか?

enum color { red, green, blue };
color c = red;
// to serialize
archive << (int)c;
// to deserialize
int i;
archive >> i;
switch(i) {
  case 0: c = red; break;
  case 1: c = green; break;
  case 2: c = blue; break;
}
4

5 に答える 5

16

enum クラスC++0xの機能であり、C++03 にはありません。

標準 C++ では、列挙はタイプ セーフではありません。列挙型が異なる場合でも、それらは事実上整数です。これにより、異なる列挙型の 2 つの列挙値を比較できます。C++03 が提供する唯一の安全性は、ある列挙型の整数または値が別の列挙型に暗黙的に変換されないことです。さらに、基になる整数型 (整数のサイズ) を明示的に指定することはできません。それは実装定義です。最後に、列挙値のスコープは外側のスコープに限定されます。したがって、2 つの別個の列挙が一致するメンバー名を持つことはできません。C++0x では、これらの問題のない列挙の特別な分類が可能になります。これは、enum クラス宣言を使用して表現されます。

例 (ウィキペディアの記事から):

enum Enum1;                   //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int;    //Legal in C++0x.
enum class Enum3;             //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short;  //Illegal in C++0x, because Enum2 was previously declared with a different type.

シリアライゼーションの部分 (元の質問の一部ではなかったと思います) については、名前は通常、整数値よりも安定しているため、列挙型アイテムを同等の文字列に変換する (および元に戻す) ヘルパー クラスを作成することを好みます。コードの動作を変更せずに列挙項目を並べ替えることができる (場合によっては並べ替える) ためです。

于 2009-05-12T15:32:27.433 に答える
14

私の古い答えがとても面倒だったので、新しい答えを作ることにしました。とにかく、これを使用して列挙型の基になる型を取得できる C++11 について何か言いたいことがあります。

std::underlying_type_t<E>

興味深いことに、オーバーロード解決のアイデアです。ただし、@lothar によって提案されているように、名前を使用して列挙を保存してください。

オーバーロードの解決は、基になる型のすべての値を表すことができる、列挙型から int、unsigned int、long、unsigned long の最初の値への 1 つの昇格が存在するという事実に由来します。他の整数型への変換はランクが低く、オーバーロードの解決では優先されません。

char (& f(int) )[1];
char (& f(unsigned int) )[2];

char (& f(long) )[3];
char (& f(unsigned long) )[4];

char const* names[] = { 
    "int", "unsigned int", 
    "long", "unsigned long"
};

enum a { A = INT_MIN };
enum b { B = UINT_MAX };
enum c { C = LONG_MIN };
enum d { D = ULONG_MAX };

template<typename T> void print_underlying() {
    std::cout << names[sizeof(f(T()))-1] << std::endl;
}

int main() { 
    print_underlying<a>();
    print_underlying<b>();
    print_underlying<c>();
    print_underlying<d>();
}

そして、これをここに出力します:

int
unsigned int
int
unsigned int

このシリアル化の問題には特に関心はありませんが (シリアル化されたデータのサイズは一定幅ではなく、列挙型とその基になる型が変更されたときに問題が発生する可能性があるため)、一般的に、型を格納する方法を理解することは興味深いことです。列挙の全体。乾杯!

于 2009-05-12T17:04:01.497 に答える
5

私は C++0x のものを読んだことがないので、それについてコメントできませんでした。

シリアル化に関しては、列挙型を読み戻すときにスイッチは必要ありません。列挙型にキャストするだけです。

ただし、ストリームへの書き込み時にはキャストしません。これは、enum に対して operator<< を記述することがよくあるためです。これにより、書き込まれている不適切な値を検出したり、代わりに文字列を書き出すことにしたりできます。

enum color { red, green, blue };
color c = red;

// to serialize
archive << c;    // Removed cast

// to deserialize
int i;
archive >> i;
c = (color)i;    // Removed switch
于 2009-05-12T16:23:24.700 に答える
5
#include <type_traits>

enum a { bla1, bla2 };
typedef typename std::underlying_type<a>::type underlying_type;

if (std::is_same<underlying_type, int>::value)
  std::cout << "It's an int!" << endl;
else if (std::is_same<underlying_type, unsigned int>::value)
  std::cout << "It's an uint!" << endl;
于 2012-07-11T19:02:40.380 に答える
1

についてenum class color: これは C++/CLI (C++ .NET) ですか、それとも将来の C++0x コードですか?

シリアル化の場合、列挙型のサイズを取得して、sizeof(color)コピーするバイト数を知ることができます。

于 2009-05-12T15:47:36.787 に答える