C ++での列挙型の前方宣言


265

私は次のようなことをやろうとしています:

enum E;

void Foo(E e);

enum E {A, B, C};

コンパイラはこれを拒否します。私はグーグルをざっと見てみましたが、コンセンサスは「あなたにはできません」のようですが、なぜなのか理解できません。誰か説明できますか?

明確化2:私は、この列挙型を取得するクラスにプライベートメソッドがあり、列挙型の値を公開したくないため、これを行っています。たとえば、Eが次のように定義されていることを誰にも知られたくない

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

プロジェクトXはユーザーに知らせたいものではないからです。

したがって、ヘッダーファイルにプライベートメソッドを置き、cppで内部的に列挙型を宣言し、ビルドされたライブラリファイルとヘッダーを人々に配布できるように、列挙型を転送宣言したかったのです。

コンパイラに関しては-それはGCCです。


これまでに何年もかかり、どういうわけかStackOverflowは私を引き戻しました;)死後の提案として- 特にあなたが説明するシナリオではこれを行わないでください 。私は抽象的なインターフェイスを定義し、これをユーザーに公開し、列挙型の定義と他のすべての実装の詳細を、他の誰も私の側で見ない内部実装で保持して、いつでも何でもできるようにし、ユーザーがいつ見るかを完全に制御できるようにしたいと思います何でも。
RnR

回答:


217

enumを前方宣言できない理由は、値を知らないと、enum変数に必要なストレージをコンパイラが知ることができないためです。C ++コンパイラは、指定されたすべての値を格納するために必要なサイズに基づいて、実際のストレージスペースを指定できます。表示されるものがすべて前方宣言である場合、変換ユニットは、選択されているストレージサイズを知ることができません。charまたはint、あるいはその他の可能性があります。


ISO C ++標準のセクション7.2.5から:

列挙基になる型は、列挙で定義されているすべての列挙子の値を表すことができる整数型です。列挙型intの値がintorに収まらない場合を除いて、基になる型が大きくならないことを除いて、列挙の基になる型として使用される整数型は実装定義unsigned intです。場合列挙リストが空で列挙が値0を有する単一の列挙子を持っていたかのように、基礎となるタイプの値であり、sizeof()列挙型、列挙型、または列挙のオブジェクトに適用される、の値はsizeof()に適用します基礎となるタイプ。

関数の呼び出し元は、コールスタックを正しくセットアップするためにパラメーターのサイズを知っている必要があるため、関数プロトタイプの前に、列挙リスト内の列挙の数を知っておく必要があります。

更新:C ++ 0Xでは、列挙型を前方宣言する構文が提案され、受け入れられました。あなたはhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdfで提案を見ることができます


29
-1。あなたの推論は正しくありません-そうでなければ、なぜあなたは「クラスC」を前方宣言することが許されるのですか?次に、Cを完全に定義する前に、Cを取得または返す関数プロトタイプを宣言しますか?
j_random_hacker 2009年

112
@j_random:完全に定義されるまで、クラスを使用することはできません。そのクラスへのポインターまたは参照のみを使用できます。これは、そのサイズと操作方法がクラスに依存しないためです。
RnR

27
クラスオブジェクトへの参照またはポインタのサイズは、コンパイラによって設定され、オブジェクトの実際のサイズとは無関係です。これは、ポインタと参照のサイズです。enumはオブジェクトであり、コンパイラが正しいストレージにアクセスするには、そのサイズが必要です。
KJAWolf 2009

16
論理的には、クラスで行うことができるように、列挙型を前方宣言している場合、列挙型へのポインタ/参照を宣言することができます。それはあなたが列挙型へのポインタを頻繁に扱わないということだけです:)
Pavel Minaev 2009

20
私はこの議論がずっと前に終わったことを知っていますが、ここで@j_random_hackerに合わせなければなりません:ここでの問題は、不完全な型へのポインターまたは参照に関するものではなく、宣言における不完全な型の使用に関するものです。行うことは合法であるためstruct S; void foo(S s);foo宣言されているだけであり、定義されていないことに注意してください)、同じenum E; void foo(E e);ようにできなかった理由はありません。どちらの場合も、サイズは必要ありません。
Luc Touraille、2010年

200

C ++ 11以降、列挙型の前方宣言が可能です。以前は、列挙型を前方宣言できなかった理由は、列挙のサイズがその内容に依存するためです。列挙のサイズがアプリケーションによって指定されている限り、それは前方宣言できます。

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.

1
この機能に対するコンパイラのサポートはありますか?GCC 4.5にはそれがないようです:(
rubenvb

4
@rubenvb同様に、Visual C ++ 11(2012)blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
knatten

私はenum32_tを探していましたが、あなたの答えでenum XXX:uint32_t {a、b、c};
ファンタジー

スコープ付き列挙型(列挙型クラス)はC ++ 11で実装されていると思いましたか?もしそうなら、それらはC ++ 0Xでどのように合法ですか?
Terrabits 2016年

1
C ++ 0xは、正式に標準化される前は、C ++ 11、@ Terrabitsの実用的な名前でした。ロジックは、機能が更新された標準に含まれることがわかっている(または可能性が高い)場合、標準が正式にリリースされる前にその機能を使用すると、実際の名前が使用される傾向があるということです。(たとえば、2011年の正式な標準化の前にC ++ 11機能をサポートしていたコンパイラはC ++ 0xをサポートし、正式な標準化の前にC ++ 17機能をサポートしたコンパイラはC ++ 1zをサポートし、C ++ 20機能をサポートするコンパイラ現在(2019)はC ++ 2aをサポートしています。)
ジャスティンタイム-モニカを

79

最近の進展を踏まえて、ここに最新の回答を追加します。

C ++ 11では、同時に列挙型のストレージ型を宣言する限り、列挙型を前方宣言できます。構文は次のようになります。

enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}

実際、関数が列挙の値を参照しない場合は、その時点で完全な宣言を行う必要はありません。

これはG ++ 4.6以降(-std=c++0xまたは-std=c++11より最近のバージョン)でサポートされています。Visual C ++ 2013はこれをサポートしています。以前のバージョンでは、私はまだ理解していない、ある種の非標準のサポートがあります-単純な前方宣言は合法であるという提案を見つけましたが、YMMVです。


4
+1は、宣言だけでなく定義でも型を宣言する必要があると述べている唯一の回答であるためです。
turoni 2017

初期のMSVCでの部分的なサポートは、少なくとも私が正しく覚えていれば、C ++ / CLIからenum classC ++拡張機能として(C ++ 11が異なる前に)バックポートされたenum classと思います。コンパイラーは列挙型の基礎となる型を指定することを許可しましたが、列挙型をサポートenum classまたは前方宣言しませんでした。列挙型のスコープで列挙子を修飾することは非標準の拡張であることを警告しました。警告を抑制しなければならなかったためにさらに煩わしいことを除いて、C ++ 11で基になる型を指定するのとほぼ同じように機能することを覚えています。
ジャスティン時間-モニカを復活させる

30

C ++での前方宣言は、コンパイル時間を劇的に高速化するので非常に便利です。あなたが前方を含むCのいくつかのものを++宣言することができますstructclassfunction、等...

しかしenum、C ++で前方宣言できますか?

いいえ、できません。

しかし、それを許可しないのはなぜですか?許可されている場合はenum、ヘッダーファイルで型を定義enumし、ソースファイルで値を定義できます。それは許されるべきだと思いませんか?

違う。

C ++ではenum、C#(int)にあるようなデフォルトの型はありません。C ++では、enum型はコンパイラによって、の値の範囲に適合する任意の型であると判断されますenum

どういう意味ですか?

つまり、enumのすべての値がenum定義されるまで、の基になる型を完全に決定することはできません。の宣言と定義を切り離すことができないのはどのような人ですかenum。したがってenum、C ++で前方宣言することはできません。

ISO C ++標準S7.2.5:

列挙型の基になる型は、列挙型で定義されているすべての列挙子の値を表すことができる整数型です。列挙型intの値がintorに収まらない場合を除いて、基になる型が大きくならないことを除いて、列挙の基になる型として使用される整数型は実装定義unsigned intです。enumerator-listが空の場合、基になるタイプは、列挙型に値0の単一の列挙子があるかのようになります。sizeof()列挙型に適用される値、列挙型のオブジェクト、または列挙子はsizeof()、基礎となるタイプ。

sizeof演算子を使用して、C ++で列挙型のサイズを決定できます。列挙型のサイズは、その基礎となる型のサイズです。このようにして、コンパイラがに使用してenumいるタイプを推測できます。

次のようにenum明示的にタイプを指定するとどうなりますか?

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

その後、あなたはあなたをあなたに宣言することができますenumか?

いいえ。でも、なぜでしょうか。

のタイプの指定は、enum実際には現在のC ++標準の一部ではありません。これはVC ++拡張機能です。ただし、C ++ 0xの一部になります。

ソース


15
この回答は数年古くなっています。
トム

時間は私たち全員をばかにします。コメントは数年古くなっています。10年の答え!
pjcard

14

[私の答えは間違っていますが、コメントが役に立つため、ここに残しました]。

異なる列挙型へのポインターが同じサイズであることが保証されていないため、列挙型の前方宣言は非標準です。コンパイラーは、このタイプで使用できるポインターのサイズを知るために、定義を確認する必要がある場合があります。

実際には、少なくともすべての一般的なコンパイラでは、列挙型へのポインタは一貫したサイズです。enumの前方宣言は、たとえばVisual C ++によって言語拡張として提供されます。


2
-1。あなたの推論が正しかった場合、同じ推論は、クラス型の前方宣言がそれらの型へのポインターを作成するために使用できないことを意味しますが、それは可能です。
j_random_hacker 2009年

6
+1。推論は正しいです。特定のケースは、sizeof(char *)> sizeof(int *)のプラットフォームです。範囲によっては、どちらも列挙型の基礎となる型になる場合があります。クラスには基礎となる型がないため、類推は誤りです。
MSalters 2009年

3
@MSalters:例:「struct S {int x;};」ここで、はsizeof(S *)がなければならない C ++のような...ポインタ前Sの定義に宣言して使用することを可能にするので、他へのポインタ構造体のサイズと等しくなるよう
j_random_hacker

1
@MSalters:... sizeof(char *)> sizeof(int *)のプラットフォームでは、この特定の構造体にこのような「フルサイズ」のポインタを使用すると効率が悪くなる可能性がありますが、コーディングが大幅に簡素化されます-まったく同じです列挙型については、これを行うことができます。
j_random_hacker 2009年

4
データへのポインターと関数へのポインターは異なるサイズにすることができますが、データポインターはラウンドトリップ(別のデータポインタータイプにキャストしてから、元に戻す必要がある)する必要があると確信しています。データポインタは同じサイズです。
Ben Voigt

7

確かに、列挙型の前方宣言のようなものはありません。enumの定義には、enumを使用する他のコードに依存する可能性のあるコードが含まれていないため、最初に宣言するときにenumを完全に定義しても問題はありません。

enumの唯一の用途がプライベートメンバー関数である場合、enum自体をそのクラスのプライベートメンバーとして持つことにより、カプセル化を実装できます。列挙型は宣言時、つまりクラス定義内で完全に定義する必要があります。ただし、プライベートメンバー関数をそこで宣言するため、これは大きな問題ではなく、実装内部の詳細な説明ではありません。

実装の詳細をより深く隠す必要がある場合は、純粋な仮想関数のみで構成される抽象インターフェースと、インターフェースを実装(継承)する具体的で完全に隠されたクラスに分割できます。クラスインスタンスの作成は、インターフェイスのファクトリまたは静的メンバー関数で処理できます。そうすれば、実際のクラス名でさえ、そのプライベート関数は言うまでもなく、公開されません。


5

その理由、実際に列挙型のサイズは前方宣言の後でまだ知られていないということです。まあ、あなたは構造体の前方宣言を使用して、ポインタを渡したり、前方宣言された構造体定義自体で参照されている場所からオブジェクトを参照したりできるようにします。

enumを値渡しできるようにしたいので、enumの前方宣言はあまり役に立ちません。私は最近、いくつかのプラットフォームがintやlongよりもcharに異なるサイズのポインターを使用するように言われたので、それへのポインターさえも持つことができませんでした。したがって、すべては列挙型の内容に依存します。

現在のC ++標準では、次のようなことを明示的に禁止しています

enum X;

(で7.1.5.3/1)。しかし、来年にC ++標準の次の問題は、実際に私を納得させ、次の、ことができます持っている基本的なタイプで行うことを。

enum X : int;

「不透明な」列挙型宣言として知られています。次のコードでは、X by valueを使用することもできます。そして、その列挙子は、後で列挙の再宣言で定義できます。7.2現在の草案を参照してください。


4

私はこのようにします:

[公開ヘッダー]

typedef unsigned long E;

void Foo(E e);

[内部ヘッダー]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

FORCE_32BITを追加することで、Econtentが長いコンパイルになることを保証し、Eと交換可能になります。


1
もちろん、これは(A)EとEcontentのタイプが異なり、(B)LP64システムではsizeof(E)= 2 * sizeof(EContent)であることを意味します。簡単な修正:ULONG_MAX。読みやすくなっています。
MSalters 2009年

2

enumをヘッダーファイルに表示したくない場合、それがプライベートメソッドでのみ使用されるようにするには、pimplの原則を使用する方法があります。

これは、次のように宣言するだけで、ヘッダーのクラス内部を確実に非表示にする手法です。

class A 
{
public:
    ...
private:
    void* pImpl;
};

次に、実装ファイル(cpp)で、内部の表現となるクラスを宣言します。

class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};

クラスコンストラクターで動的に実装を作成し、デストラクターで削除する必要があります。パブリックメソッドを実装する場合は、以下を使用する必要があります。

((AImpl*)pImpl)->PrivateMethod();

pimplを使用することには長所があります。1つは、実装からクラスヘッダーを切り離すことです。1つのクラスの実装を変更するときに他のクラスを再コンパイルする必要はありません。もう1つは、ヘッダーが非常にシンプルであるため、コンパイル時間が短縮されることです。

しかし、これは使用するのが面倒なので、ヘッダーで列挙型をプライベートとして宣言するだけでそれほど問題になるかどうかを自問する必要があります。


3
struct AImpl; struct A {private:AImpl * pImpl; };

2

enumを構造体でラップし、いくつかのコンストラクターと型変換を追加して、代わりに構造体を前方宣言できます。

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

これは動作するようです:http : //ideone.com/TYtP2



1

これが(一種の)ぶつかったので、いくつかの異論がありますので、ここに標準からいくつかの関連するビットがあります。調査によると、この規格では実際に前方宣言が定義されておらず、列挙型を前方宣言できるかどうかは明示的に規定されていません。

まず、dcl.enumのセクション7.2から:

列挙型の基になる型は、列挙型で定義されているすべての列挙子の値を表すことができる整数型です。列挙型の値がintまたはunsigned intに収まらない限り、基になる型がintより大きくならないことを除いて、どの整数型が列挙の基になる型として使用されるかは、実装定義です。enumerator-listが空の場合、基礎となる型は、列挙に値0の単一の列挙子があるかのようになります。列挙型、列挙型のオブジェクト、または列挙子に適用されるsizeof()の値は、基本となる型に適用されるsizeof()。

したがって、列挙型の基礎となる型は実装定義であり、1つの小さな制限があります。

次に、「不完全型」(3.9)のセクションに移動します。これは、前方宣言の標準にほぼ近づいています。

宣言されているが定義されていないクラス、またはサイズが不明な要素タイプまたは不完全な要素タイプの配列は、不完全に定義されたオブジェクトタイプです。

クラスタイプ(「クラスX」など)は、翻訳単位のある時点で不完全で、後で完了する場合があります。タイプ「クラスX」は両方の点で同じタイプです。配列オブジェクトの宣言された型は、不完全なクラス型の配列であり、したがって不完全な場合があります。クラスタイプが後で変換ユニットで完了すると、配列タイプは完全になります。これらの2点の配列型は同じ型です。配列オブジェクトの宣言された型は、サイズが不明な配列である可能性があるため、変換単位のある時点で不完全であり、後で完了します。これらの2点の配列タイプ(「Tの未知の境界の配列」と「N Tの配列」)は異なるタイプです。不明なサイズの配列へのポインタの型、または不明なサイズの配列になるようにtypedef宣言によって定義された型

そこで、標準では、ほとんどの場合、前方宣言できる型をレイアウトしました。Enumはそこにはなかったので、コンパイラの作成者は、基本的に、その基本となる型のサイズが可変であるため、前方宣言を標準で許可されていないと見なしています。

それも理にかなっています。列挙型は通常、値渡しの状況で参照され、コンパイラは実際にそれらの状況でのストレージサイズを知る必要があります。ストレージサイズは実装によって定義されるため、多くのコンパイラは、すべての列挙型の基になる型に32ビット値を使用することを選択するだけでよく、その時点で、それらを前方宣言することが可能になります。興味深い実験としては、ビジュアルスタジオで列挙型を宣言してから、上記で説明したようにsizeof(int)を超える基礎となる型を使用して、何が起こるかを確認することが考えられます。


「enum foo;」を明示的に禁止していることに注意してください。7.1.5.3/1で(ただし、すべての場合と同様に、コンパイラが警告する限り、もちろんそのようなコードをコンパイルする可能性があります)
Johannes Schaub-litb 2009年

それを指摘してくれてありがとう、それは本当に難解な段落であり、それを解析するのに1週間かかるかもしれません。しかし、そこにあることを知っておくのは良いことです。
Dan Olson、

心配はいりません。いくつかの標準段落は本当に奇妙です:)まあ、複雑な型指定子は型を指定するものですが、それを明確にするために何かを指定することもできます。たとえば、「X」の代わりに「struct X」、または「Y」だけの代わりに「enum Y」。何かが本当にタイプであることをアサートするために必要です。
Johannes Schaub-litb 2009年

したがって、次のように使用できます: "class X * foo;" Xがまだ前方宣言されていない場合。または、曖昧さをなくすためのテンプレートで「typename X :: foo」。または「class link obj;」同じ名前のクラスをシャドウする同じスコープ内に関数「リンク」がある場合。
Johannes Schaub-litb 2009年

3.4.4では、非タイプ名がタイプ名を隠す場合に使用されると述べています。「クラスX」のような前方宣言は別として、これらが最もよく使用されます。(ここでは宣言の唯一の構成要素です)。ここでは非テンプレートでそれらについて話します。ただし、14.6 / 3はテンプレートでのそれらの使用法を示しています。
Johannes Schaub-litb 2009年

1

VCの場合、前方宣言と基になる型の指定に関するテストは次のとおりです。

  1. 次のコードは問題なくコンパイルされています。
    typedef int myint;
    列挙型T;
    void foo(T * tp)
    {
        * tp =(T)0x12345678;
    }
    列挙型T:char
    {
        あ
    };

しかし/ W4の警告を受けました(/ W3はこの警告を受けません)

警告C4480:非標準の拡張機能が使用されています:enum 'T'の基になる型を指定しています

  1. 上記の場合、VC(Microsoft(R)32ビットC / C ++最適化コンパイラバージョン15.00.30729.01 for 80x86)はバグがあるように見えます。

    • 列挙型Tを参照するとき; VCは、列挙型Tがデフォルトの4バイトintを基本型として使用することを前提としているため、生成されるアセンブリコードは次のとおりです。
    ?foo @@ YAXPAW4T @@@ Z PROC; foo
    ; ファイルe:\ work \ c_cpp \ cpp_snippet.cpp
    ; 行13
        ebpをプッシュ
        mov ebp、esp
    ; 行14
        mov eax、DWORD PTR _tp $ [ebp]
        mov DWORD PTR [eax]、305419896、12345678H
    ; 行15
        ポップebp
        ret 0
    ?foo @@ YAXPAW4T @@@ Z ENDP; foo

上記のアセンブリコードは/Fatest.asmから直接抽出されたものであり、個人的な推測ではありません。mov DWORD PTR [eax]、305419896が表示されますか。12345678Hライン?

次のコードスニペットはそれを証明します。

    int main(int argc、char * argv)
    {
        ユニオン{
            char ca [4];
            T t;
        } a;
        a.ca [0] = a.ca [1] = a。[ca [2] = a.ca [3] = 1;
        foo(&a.t);
        printf( "%#x、%#x、%#x、%#x \ n"、a.ca [0]、a.ca [1]、a.ca [2]、a.ca [3]) ;
        0を返します。
    }

結果は0x78、0x56、0x34、0x12です。

  • enum Tの前方宣言を削除し、関数fooの定義をenum Tの定義の後に移動します。結果はOKです。

上記の重要な指示は次のようになります。

mov BYTE PTR [eax]、120; 00000078H

最終結果は0x78、0x1、0x1、0x1です。

値が上書きされていないことに注意してください

したがって、VCでの列挙型の前方宣言の使用は有害であると見なされます。

ちなみに、当然のことながら、基になる型の宣言の構文はC#の構文と同じです。実際には、組み込みシステムと通信するときに、基になる型をcharとして指定することで3バイトを節約する価値があることがわかりました。これは、メモリが限られているためです。


1

私のプロジェクトでは、ネームスペースバインド列挙手法を採用して、enumレガシーコンポーネントとサードパーティコンポーネントのs を処理しました。次に例を示します。

forward.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}

foo.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}

main.cc:

#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}

foo.hヘッダーはについて何も知る必要がないことに注意してくださいlegacy::evil。レガシータイプlegacy::evil(ここではmain.cc)を使用するファイルのみを含める必要がありますenum.h


0

あなたの問題に対する私の解決策は次のいずれかです:

1-列挙型の代わりにintを使用:CTPファイルの(ヘッダーではなく)匿名の名前空間でintを宣言します。

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

メソッドはプライベートなので、誰もデータをいじることはありません。さらに、誰かが無効なデータを送信したかどうかをテストすることもできます。

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2:Javaの場合と同様に、constのインスタンス化が制限された完全なクラスを作成します。Forwardはクラスを宣言し、CPPファイルで定義して、列挙型の値のみをインスタンス化します。私はC ++でそのようなことをしましたが、列挙型(コピー構築、演算子=など)をシミュレートするためにいくつかのコードが必要だったため、結果は期待どおりにはなりませんでした。

3:以前に提案されたように、プライベートに宣言された列挙型を使用します。ユーザーは完全な定義を見ることができますが、それを使用したり、プライベートメソッドを使用したりすることはできません。したがって、通常は、クラスを使用してコードを再コンパイルする必要なく、既存のメソッドの列挙型とコンテンツを変更できます。

私の推測は、ソリューション3または1のどちらかでしょう。


-1

enumはさまざまなサイズの整数サイズになる可能性があるため(コンパイラーは特定のenumのサイズを決定します)、enumへのポインターもさまざまなサイズになる可能性があります。例えば)。

そのため、コンパイラは、列挙型をフォワード宣言して、それへのポインタをユーザーに使用させることさえできません。そこにさえ、列挙型のサイズが必要だからです。


-1

列挙を定義して、タイプの要素の可能な値を制限されたセットに制限します。この制限はコンパイル時に適用されます。

後で「制限されたセット」を使用するという事実を前方に宣言しても、値は追加されません。後続のコードは、その恩恵を受けるために可能な値を知る必要があります。

コンパイラー列挙型のサイズを懸念しますが、それを前方宣言すると、列挙型の意図が失われます。


1
いいえ、後続のコードは、これが役立つ値を知っている必要はありません。特に、後続のコードが列挙型パラメーターを取得または返す関数プロトタイプにすぎない場合は、型のサイズは重要ではありません。ここで前方宣言を使用すると、ビルドの依存関係を削除し、コンパイルを高速化できます。
j_random_hacker

あなたが正しい。意図は値に従うのではなく、タイプに従うことです。0x列挙型で解決されました。
xtofl 2009年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.