web-dev-qa-db-ja.com

C + +で 'struct'と 'typedef struct'の違いは?

C++では、次の点に違いがありますか。

struct Foo { ... };

そして

typedef struct { ... } Foo;
765
criddell

C++では、わずかな違いがあります。それはCからのホールドオーバーです、それはそれが違いを生むということです。

C言語規格( C89§3.1.2.3C99§6.2.3 、および C11§6.2.3 )では、 タグを含め、さまざまなカテゴリの識別子に対して別々の名前空間が義務付けられています。識別子 structname __/unionname __/enumname__)および 通常の識別子 typedefname__およびその他の識別子の場合).

あなたがちょうど言ったなら:

struct Foo { ... };
Foo x;

Fooname__はタグネームスペースでのみ定義されているため、コンパイラエラーが発生します。

次のように宣言する必要があります。

struct Foo x;

Fooname__を参照したいときはいつでも、struct Fooと呼ばなければなりません。これは面倒なので、typedefname__を追加することができます。

struct Foo { ... };
typedef struct Foo Foo;

これで、struct Foo(タグ名前空間内)と単なるFooname__(通常の識別子名前空間内)の両方が同じことを参照するようになり、Fooname__キーワードなしでstructname__型のオブジェクトを自由に宣言できます。


コンストラクト:

typedef struct Foo { ... } Foo;

宣言とtypedefname__の単なる省略形です。


最後に、

typedef struct { ... } Foo;

無名構造体を宣言し、それにtypedefname__を作成します。したがって、この構成では、タグ名前空間に名前がなく、typedef名前空間に名前があるだけです。これはまた、それを前方宣言することができないことを意味します。 もし前方宣言をしたいのなら、タグ名前空間 に名前を付けなければなりません。


C++では、名前が同じ名前の別の宣言によって隠されていない限り、すべてのstructname __/unionname __/enumname __/classname__宣言は暗黙のうちにtypedefname__宣言されているように動作します。詳細は Michael Burrの答え をご覧ください。

1106
Adam Rosenfield

このDDJの記事 で、Dan Saksは、あなたがあなたの構造体(およびクラス!)をtypedefしないとバグが突き抜けることができる1つの小さな領域を説明します。

必要に応じて、C++がすべてのタグ名に対してtypedefを生成すると想像することができます。

typedef class string string;

残念ながら、これは完全に正確というわけではありません。私はそれがそれほど単純であったことを望みますが、そうではありません。 C++は、Cとの非互換性を導入せずに、構造体、共用体、または列挙型に対してそのようなtypedefを生成することはできません。

たとえば、Cプログラムが関数とstatusという名前の構造体の両方を宣言しているとします。

int status(); struct status;

繰り返しますが、これは悪い習慣かもしれませんが、それはCです。このプログラムでは、statusは(それ自体で)関数を参照しています。 struct statusは型を表します。

C++がタグのtypedefを自動的に生成した場合、このプログラムをC++としてコンパイルしたときに、コンパイラは次のように生成します。

typedef struct status status;

残念ながら、この型名は関数名と競合し、プログラムはコンパイルされません。そのため、C++では各タグのtypedefを単純に生成できません。

C++では、タグはtypedef名とまったく同じように機能しますが、プログラムはタグと同じ名前および同じスコープを持つオブジェクト、関数、または列挙子を宣言できます。その場合、オブジェクト、関数、または列挙子の名前はタグ名を隠します。プログラムは、タグ名の前にキーワードクラス、構造体、共用体、または列挙型(必要に応じて)を使用することによってのみタグ名を参照できます。これらのキーワードの1つとそれに続くタグからなる型名は、精巧な型指定子です。例えば、struct statusとenum monthは精巧な型指定子です。

したがって、両方を含むCプログラム

int status(); struct status;

c ++としてコンパイルしたときと同じ動作をします。名前の状態だけで機能を参照します。プログラムは、型指定のstruct statusステータスを使用することによってのみ型を参照できます。

それではこれはどのようにバグがプログラムに入り込むことを可能にしますか? リスト1 のプログラムを考えてみましょう。このプログラムは、デフォルトのコンストラクタと、fooオブジェクトをchar const *に変換する変換演算子を使ってクラスfooを定義します。表現

p = foo();

in 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++のクラスに拡張してはいけません。確かに、あなたは間違いを訂正することができますが、それは必要ではないはずの余分なプログラミングの訓練と努力を必要とします。

216
Michael Burr

もう1つ重要な違い:typedefname__sは前方宣言できません。そのため、typedefname__オプションを使用するには、typedefname__を含むファイルを#includeする必要があります。つまり、#includeを直接必要とするかどうかにかかわらず、.hに含まれるすべてのファイルにそのファイルも含まれます。大規模プロジェクトでのビルド時間に間違いなく影響を与える可能性があります。

typedefname__がないと、場合によっては、struct Foo;ファイルの先頭に.hの前方宣言を追加し、#includeファイルの構造体定義にのみ.cppを追加することができます。

62
Joe

そこに ある 違いがありますが微妙です。このように見てください。struct Fooは新しい型を導入します。 2番目のものは、名前のないstruct型のためにFooと呼ばれる(新しい型ではない)別名を作成します。

7.1.3 typedef指定子

1 [...]

Typedef指定子で宣言された名前はtypedef名になります。その宣言の範囲内では、typedef-nameはキーワードと構文的に同等であり、第8項で説明されている方法で識別子に関連付けられた型を指定します。したがって、typedef-nameは別の型の同義語です。 typedef-name新しい型を導入しないクラス宣言(9.1)またはenum宣言の方法.

8 typedef宣言が名前なしクラス(またはenum)を定義している場合、宣言によってそのクラス型(またはenum型)として宣言された最初のtypedef-nameは、リンクの目的でのみクラス型(またはenum型)を示すために使用されます。 3.5)。 [例:

typedef struct { } *ps, S; // S is the class name for linkage purposes

そのため、typedef 常に は別の型のプレースホルダー/同義語として使用されます。

31
dirkgently

Typedef構造体と一緒に前方宣言を使用することはできません。

構造体自体は匿名型なので、前方宣言するための実際の名前はありません。

typedef struct{
    int one;
    int two;
}myStruct;

このような前方宣言はうまくいきません。

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types
10
Yochai Timmer

構造体はデータ型を作成することです。 typedefは、データ型のニックネームを設定するためのものです。

1
David Tu

'typedef構造体'とC++の '構造体'の重要な違いは、 '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; };
0
user2796283