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
、それを a と呼ぶ必要がありstruct Foo
ます。これはすぐに面倒になるので、次を追加できますtypedef
。
struct Foo { ... };
typedef struct Foo Foo;
現在struct Foo
(タグ名前空間内) と単純な(通常の識別子名前空間内) はどちらも同じものを参照しており、キーワードなしFoo
でタイプのオブジェクトを自由に宣言できます。Foo
struct
コンストラクト:
typedef struct Foo { ... } Foo;
は、宣言 and の単なる省略形ですtypedef
。
ついに、
typedef struct { ... } Foo;
匿名構造を宣言し、そのための を作成しtypedef
ます。したがって、この構成では、タグの名前空間には名前がなく、typedef 名前空間にのみ名前があります。これは、前方宣言もできないことを意味します。 前方宣言を行いたい場合は、タグの名前空間で名前を付ける必要があります。
C++ では、すべてのstruct
// union
/宣言は、名前が同じ名前の別の宣言によって隠されていない限り、暗黙的に 'ed であるかのように動作enum
します。詳細については、 Michael Burr の回答を参照してください。class
typedef
このDDJ 記事で、Dan Saks は、構造体 (およびクラス!) を型定義しない場合にバグが忍び寄る可能性のある小さな領域について説明しています。
必要に応じて、C++ がすべてのタグ名に対して typedef を生成することを想像できます。
typedef class string string;
残念ながら、これは完全に正確ではありません。そんなに単純だといいのですが、そうではありません。C++ は、C との非互換性を導入しない限り、構造体、共用体、または列挙型のそのような型定義を生成できません。
たとえば、C プログラムが関数と status という名前の構造体の両方を宣言するとします。
int status(); struct status;
繰り返しますが、これは悪い習慣かもしれませんが、C です。このプログラムでは、ステータス (それ自体) は関数を参照します。struct status は型を参照します。
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++ としてコンパイルした場合と同じように動作します。名前 status だけが機能を参照します。プログラムは、詳細型指定子構造体ステータスを使用してのみ型を参照できます。
では、これはどのようにしてバグがプログラムに忍び込むことを可能にするのでしょうか? リスト 1のプログラムについて考えてみましょう 。このプログラムは、デフォルトのコンストラクターを持つクラス foo と、foo オブジェクトを char const * に変換する変換演算子を定義します。表現
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++ のクラスに拡張すべきではありませんでした。確かに、間違いを修正することはできますが、余分なプログラミングの規律と必要ではない努力が必要になります。
もう 1 つの重要な違い: typedef
s は前方宣言できません。したがって、typedef
オプションの場合は、 を含むファイルが必要#include
です。つまり、直接必要かどうかに関係なく、そのファイルが含まれているtypedef
ことを意味し#include
ます。.h
大規模なプロジェクトでは、ビルド時間に確実に影響を与える可能性があります。
がなければ、typedef
場合によっては、ファイルstruct Foo;
の先頭に の前方宣言を追加し、ファイル内の構造体定義.h
のみを追加できます。#include
.cpp
違いはありますが微妙です。このように見てください:struct Foo
新しい型を導入します。2 番目のものは、名前のない型の Foo (新しい型ではない) というエイリアスを作成しますstruct
。
7.1.3 typedef 指定子
1 [...]
typedef 指定子で宣言された名前は、typedef 名になります。その宣言の範囲内では、typedef-name は構文的にキーワードと同等であり、8 節で説明されている方法で識別子に関連付けられた型に名前を付けます。したがって、typedef-name は別の型の同義語です。typedef-nameは、クラス宣言 (9.1) または enum 宣言のように新しい型を導入しません。
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
'typedef struct' と C++ の 'struct' の重要な違いは、'typedef structs' のインライン メンバ初期化が機能しないことです。
// 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; };
C++ には違いはありませんが、C では、明示的に行わなくても構造体 Foo のインスタンスを宣言できると思います。
struct Foo bar;