使用できるのに、なぜ人々はC ++の列挙型を定数として使用するのconst
ですか?
12 に答える
Bruce Eckel はThinking in C++ で理由を述べています。
C++ の古いバージョンでは、
static const
はクラス内でサポートされていませんでした。これは、const
クラス内の定数式には役に立たないことを意味していました。しかし、人々はまだこれをやりたがっていたので、典型的な解決策 (通常は「enum ハック」と呼ばれます) は、enum
インスタンスなしでタグなしを使用することでした。列挙型は、コンパイル時に確立されたすべての値を持っている必要があり、クラスに対してローカルであり、その値は定数式で使用できます。したがって、一般的に次のように表示されます。
#include <iostream> using namespace std; class Bunch { enum { size = 1000 }; int i[size]; }; int main() { cout << "sizeof(Bunch) = " << sizeof(Bunch) << ", sizeof(i[1000]) = " << sizeof(int[1000]) << endl; }
列挙型は個別の型であるため、オーバーロードなどの型指向の操作を実行できます。
enum Color { Red,Green,Blue };
enum Size { Big,Little };
void f( Color c ) {
}
void f( Size s ) {
}
int main() {
f( Red );
f( Big );
}
列挙は、関連する定数のセットを意味するため、関係に関する追加情報は、目前の問題のモデルに役立つはずです。
テンプレートのメタプログラミングを扱う場合にも、歴史的な理由があります。一部のコンパイラは列挙型の値を使用できますが、静的 const int を使用してクラスをインスタンス化することはできませんでした。
template <int N>
struct foo
{
enum { Value = foo<N-1>::Value + N };
};
template <>
struct foo<0>
{
enum { Value = 0; }
};
これで、より賢明な方法でそれを行うことができます:
template <int N>
struct foo
{
static const int Value = foo<N-1>::Value + N;
};
template <>
struct foo<0>
{
static const int Value = 0;
};
別の考えられる理由は、静的な const int は実行時にメモリを予約している可能性があるのに対し、enum は実際のメモリ位置を予約することはなく、コンパイル時に処理されることです。この関連する質問を参照してください。
列挙型を使用すると、より説明的になります。検討:
int f(int fg, int bg)
対
int f(COLOR fg, COLOR bg)
さらに、列挙型はもう少し型安全性を提供します。
- 整数は列挙型に暗黙的に変換できません
- ある型の列挙型は、別の型の列挙型に暗黙的に変換できません
たとえば、列挙型で使用できる自動動作が気に入っています。
enum {NONE, START, HEY, HO, LAST};
次に、LAST までループするのは簡単で、新しい状態 (または表現されているもの) が追加されると、ロジックが適応します。
for (int i = NONE; i < LAST; i++)
{
// Do stuff...
}
何かを追加...
enum {NONE, START, HEY, WEE, HO, LAST};
ループは適応します...
コンパイラ ベンダーが ISO/IEC 14882:1998 C++ 標準を実装する前は、クラス スコープで定数を定義する次のコードでコンパイル エラーが発生しました。
class Foo {
static const int MAX_LEN = 80;
...
};
定数が整数型の場合、面倒な回避策は、クラス内の列挙型で定義することです。
class Foo {
enum {
MAX_LEN = 80
};
...
};
列挙型は型名としても使用できます。したがって、列挙型をパラメーターとして受け取る関数を定義できます。これにより、値を const 変数として定義し、関数が「int」のみを受け入れる場合と比較して、関数の引数としてどのような種類の値を指定する必要があるかがより明確になります。引数として。
検討:
enum my_new_fangled_type {
baz = 0,
meh = 1
};
void foo (my_new_fangled_type bar) // bar can be a value listed in the enum
{
...
}
対:
int const baz = 0;
int const meh = 1;
void foo (int bar) // what are valid values for bar?
{
...
}
一部のデバッガーは、デバッグ時に値の代わりに列挙名を表示します。これは非常に役立ちます。私は私がむしろ見たいと思うことを知っていday_of_week = MONDAY
ますday_of_week = 1
。
これは、古いコンパイラが真のクラス定数の宣言をサポートしていなかったことが一因です。
class C
{
const int ARealConstant = 10;
};
だからこれをしなければならなかった
class C
{
enum { ARealConstant = 10 };
};
このため、多くのポータブルライブラリは引き続きこのフォームを使用します。
もう1つの理由は、列挙型を便利な構文デバイスとして使用して、クラス定数を関連するものと関連しないものに編成できることです。
class DirectorySearcher
{
enum options
{
showFiles = 0x01,
showDirectories = 0x02,
showLinks = 0x04,
};
};
vs
class Integer
{
enum { treatAsNumeric = true };
enum { treatAsIntegral = true };
enum { treatAsString = false };
};
列挙型を使用すると、有効な選択肢が簡潔に文書化され、コンパイラがそれらを強制できるようになります。
たとえば、Pi などの enum ストア グローバル定数を使用している場合、その目的が何であるかはわかりません。
理由の 1 つは、const
より多くの入力が必要になることです。
enum { Val1, Val2, Val3 };
...対...
const int Val1=0, Val2=1, Val3=2;