ではC ++、間に違いがあります:
struct Foo { ... };
そして:
typedef struct { ... } Foo;
ではC ++、間に違いがあります:
struct Foo { ... };
そして:
typedef struct { ... } Foo;
回答:
C ++では、微妙な違いしかありません。それは違いを生み出すCからの持ち越しです。
C言語標準(C89§3.1.2.3、C99§6.2.3、およびC11§6.2.3)では、タグ識別子(struct
/ union
/の場合enum
)と通常の識別子(typedef
他の識別子の場合)を含む、識別子のさまざまなカテゴリに個別の名前空間を義務付けています。。
あなたが言っただけなら:
struct Foo { ... };
Foo x;
Foo
はタグの名前空間でのみ定義されているため、コンパイラエラーが発生します。
次のように宣言する必要があります。
struct Foo x;
を参照するFoo
ときはいつでも、それを常にと呼ぶ必要がありstruct Foo
ます。これはすぐに煩わしいので、以下を追加できますtypedef
。
struct Foo { ... };
typedef struct Foo Foo;
これでstruct Foo
(タグネームスペース内)とプレーンFoo
(通常の識別子ネームスペース内)の両方が同じものを参照するようになりFoo
、struct
キーワードなしでタイプのオブジェクトを自由に宣言できます。
構成:
typedef struct Foo { ... } Foo;
は宣言の省略形ですtypedef
。
最終的に、
typedef struct { ... } Foo;
匿名構造を宣言し、typedef
それを作成します。したがって、この構成では、タグの名前空間に名前はなく、typedef名前空間の名前のみが含まれます。つまり、これを前方宣言することもできません。 前方宣言を行う場合は、タグの名前空間で名前を付ける必要があります。
C ++では、すべてのstruct
/ union
/ enum
/ class
宣言typedef
は、名前が同じ名前の別の宣言によって隠されていない限り、暗黙的に'のように動作します。詳細については、Michael Burrの回答を参照してください。
で、このDDJの記事、ダンサックスはあなたの構造体のtypedefない場合は、バグが通過クリープすることができます一つの小さなエリア説明(およびクラスを!):
必要に応じて、C ++がすべてのタグ名のtypedefを生成すると想像できます。
typedef class string string;
残念ながら、これは完全に正確ではありません。それがそんなに簡単だったらいいのにと思いますが、そうではありません。C ++は、Cとの非互換性を導入せずに、構造体、共用体、または列挙型のそのようなtypedefを生成できません。
たとえば、Cプログラムが関数とstatusという名前の構造体の両方を宣言するとします。
int status(); struct status;
繰り返しになりますが、これは悪い習慣かもしれませんが、Cです。このプログラムでは、ステータス(それ自体)は関数を指します。構造体ステータスはタイプを参照します。
C ++がタグのtypedefを自動的に生成した場合、このプログラムをC ++としてコンパイルすると、コンパイラーは以下を生成します。
typedef struct status status;
残念ながら、この型名は関数名と競合し、プログラムはコンパイルされません。これが、C ++が各タグのtypedefを単純に生成できない理由です。
C ++では、タグはtypedef名と同じように機能しますが、プログラムは、タグと同じ名前と同じスコープを持つオブジェクト、関数、または列挙子を宣言できます。その場合、オブジェクト、関数、または列挙子の名前はタグ名を非表示にします。プログラムは、タグ名の前にキーワードclass、struct、union、またはenum(適切な場合)を使用することによってのみ、タグ名を参照できます。これらのキーワードの1つにタグが続くタイプ名は、エラボレートタイプ指定子です。たとえば、struct statusとenum monthは、精巧な型指定子です。
したがって、次の両方を含むCプログラム:
int status(); struct status;
C ++としてコンパイルした場合も同じように動作します。名前のステータスのみが関数を参照します。プログラムは、精巧なタイプ指定子構造体ステータスを使用することによってのみタイプを参照できます。
では、どのようにすればバグがプログラムに侵入できるのでしょうか。リスト1のプログラムを考えてみてください 。このプログラムは、デフォルトのコンストラクターを持つクラスfooと、fooオブジェクトをchar const *に変換する変換演算子を定義します。表現
p = foo();
mainでは、fooオブジェクトを作成し、変換演算子を適用する必要があります。後続の出力ステートメント
cout << p << '\n';
クラスfooを表示するべきですが、表示しません。関数fooを表示します。
この驚くべき結果は、プログラムにリスト2に示すヘッダーlib.hが含まれているために発生します。このヘッダーは、fooという名前の関数を定義します。関数名fooはクラス名fooを隠すため、mainでのfooへの参照は、クラスではなく関数を参照します。mainは、次のように、精巧な型指定子を使用することによってのみクラスを参照できます。
p = class foo();
プログラム全体でこのような混乱を回避する方法は、クラス名fooに次のtypedefを追加することです。
typedef class foo foo;
クラス定義の直前または直後。このtypedefは、型名fooと関数名foo(ライブラリーから)の間の競合を引き起こし、コンパイル時エラーをトリガーします。
もちろん、これらのtypedefを実際に書いている人は誰もいません。それは多くの規律を必要とします。リスト1のようなエラーの発生率はおそらくかなり小さいので、多くの人がこの問題に悩まされることはありません。ただし、ソフトウェアのエラーによって人身事故が発生する可能性がある場合は、エラーが発生する可能性が低い場合でもtypedefを記述する必要があります。
クラスと同じスコープ内で関数名またはオブジェクト名を使用してクラス名を非表示にしたくなる理由は想像できません。Cの非表示規則は誤りであり、C ++のクラスに拡張されるべきではありませんでした。実際、間違いを修正することはできますが、必要ではない余分なプログラミング規則と労力が必要です。
Listing 1
とListing 2
リンクが壊れています。見てください。
もう1つの重要な違い:typedef
は前方宣言できません。そこらのtypedef
オプションあなたがしなければならない#include
ファイルが含むtypedef
すべてのものを意味し、#include
あなたはsの.h
も、それが直接それを必要とするかどうか、というようにするかどうか、そのファイルが含まれています。大規模なプロジェクトのビルド時間に確実に影響を与える可能性があります。
を使用しないtypedef
場合struct Foo;
、.h
ファイルの先頭に前方宣言を追加#include
し、.cpp
ファイル内の構造体定義のみを追加できる場合があります。
違いはありますが、微妙です。このように見てくださいstruct Foo
。新しいタイプを紹介します。2つ目は、名前のないstruct
型のFoo(新しい型ではなく)と呼ばれるエイリアスを作成します。
7.1.3 typedef指定子
1 [...]
typedef指定子で宣言された名前はtypedef-nameになります。typedef-nameは、その宣言の範囲内で、構文的にキーワードと同等であり、8節で説明した方法で識別子に関連付けられた型に名前を付けます。したがって、typedef-nameは別の型の同義語です。typedef-name は、クラス宣言(9.1)または列挙型宣言のように新しい型を導入しません。
8 typedef宣言が名前のないクラス(または列挙型)を定義する場合、宣言によってそのクラス型(または列挙型)として宣言された最初のtypedef-nameは、リンク目的でのみクラス型(または列挙型)を示すために使用されます( 3.5)。[例:
typedef struct { } *ps, S; // S is the class name for linkage purposes
そのため、typedefは常に別の型のプレースホルダー/シノニムとして使用されます。
typedef構造体では前方宣言を使用できません。
構造体自体は匿名型であるため、転送宣言する実際の名前はありません。
typedef struct{
int one;
int two;
}myStruct;
このような前方宣言は機能しません:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
myStruct
はタグ名前空間にあり、typedef_ed myStruct
は、関数名やローカル変数名などの他の識別子が存在する通常の名前空間にあります。そのため、競合があってはなりません。エラーが疑われる場合は、コードを表示できます。
typedef
持つforward宣言だけがある場合typedef
、名前のない構造を参照しないことは理解できると思います。代わりに、フォワード宣言はtagで不完全な構造を宣言しmyStruct
ます。また、の定義を見ないとtypedef
、typedef
ed名を使用する関数プロトタイプは無効です。したがってmyStruct
、型を表すために使用する必要があるときはいつでも、typedef全体を含める必要があります。私があなたを誤解した場合は私を修正してください。ありがとう。
C ++での「typedef構造体」と「構造体」の重要な違いは、「typedef構造体」でのインラインメンバーの初期化が機能しないことです。
// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;
// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };
x
も初期化されます。ColiruオンラインIDEでのテストを 参照してください(私はそれを42に初期化したので、割り当てが実際に行われたことがゼロよりも明白です)。
C ++には違いはありませんが、Cでは、明示的に行うことなくstruct Fooのインスタンスを宣言できるようになると思います。
struct Foo bar;