宣言
Declaration: typedef Decl alias Decl Decl Decl: StorageClasses Decl BasicType Declarators ; BasicType Declarator FunctionBody AutoDeclaration Declarators: DeclaratorInitializer DeclaratorInitializer , DeclaratorIdentifierList DeclaratorInitializer: Declarator Declarator = Initializer DeclaratorIdentifierList: DeclaratorIdentifier DeclaratorIdentifier , DeclaratorIdentifierList DeclaratorIdentifier: Identifier Identifier = Initializer BasicType: BasicTypeX .IdentifierList IdentifierList Typeof Typeof . IdentifierList BasicTypeX: bool byte ubyte short ushort int uint long ulong char wchar dchar float double real ifloat idouble ireal cfloat cdouble creal void .IdentifierList IdentifierList Typeof Typeof . IdentifierList BasicType2: * [ ] [ Expression ] [ Expression .. Expression ] [ Type ] delegate Parameters function Parameters Declarator: BasicType2opt ( Declarator ) DeclaratorSuffixesopt BasicType2opt Identifier DeclaratorSuffixesopt DeclaratorSuffixes: DeclaratorSuffix DeclaratorSuffix DeclaratorSuffixes DeclaratorSuffix: [ ] [ Expression ] [ Type ] TemplateParameterListopt Parameters IdentifierList: Identifier Identifier . IdentifierList TemplateInstance TemplateInstance . IdentifierList StorageClasses: StorageClass StorageClass StorageClasses StorageClass: abstract auto const deprecated extern final scope static synchronized Property: @ Identifier Type: BasicType BasicType Declarator2 Declarator2: BasicType2opt DeclaratorSuffixesopt BasicType2opt ( Declarator2 ) DeclaratorSuffixesopt Parameters: ( ParameterList ) ( ) ParameterList: Parameter Parameter , ParameterList ... Parameter: InOutopt BasicType Declarator InOutopt BasicType Declarator ... InOutopt BasicType Declarator = DefaultInitializerExpression InOutopt Type InOutopt Type ... InOut: InOutX InOut InOutX InOutX: auto const final in inout lazy out ref scope shared MemberFunctionAttributes: MemberFunctionAttribute MemberFunctionAttribute MemberFunctionAttributes MemberFunctionAttribute: const immutable inout shared FunctionAttribute DefaultInitializerExpression: AssignExpression Initializer: VoidInitializer NonVoidInitializer NonVoidInitializer: AssignExpression ArrayInitializer StructInitializer ArrayInitializer: [ ] [ ArrayMemberInitializations ] ArrayMemberInitializations: ArrayMemberInitialization ArrayMemberInitialization , ArrayMemberInitialization , ArrayMemberInitializations ArrayMemberInitialization: NonVoidInitializer AssignExpression : NonVoidInitializer StructInitializer: { } { StructMemberInitializers } StructMemberInitializers: StructMemberInitializer StructMemberInitializer , StructMemberInitializer , StructMemberInitializers StructMemberInitializer: NonVoidInitializer Identifier : NonVoidInitializer
宣言の構文
宣言の構文は、基本的に左から右へと読むことができます。
int x; // x は int int* x; // x は int へのポインタ int** x; // x は int へのポインタ へのポインタ int[] x; // x は int の配列 int*[] x; // x は int へのポインタ の配列 int[]* x; // x は int の配列 へのポインタ
配列も、左から右へ読みます:
int[3] x; // xは int の3要素配列 int[3][5] x; // xは int の3要素配列 の5要素配列 int[3]*[5] x; // xは int の3要素配列 へのポインタ の5要素配列
関数へのポインタは function キーワードを使って宣言します:
int function(char) x; // x はcharを引数としてintを返す関数 // へのポインタ int function(char)[] x; // x はcharを引数としてintを返す関数 // へのポインタ の配列
C風の宣言構文を代わりに用いることもできます:
int x[3]; // x は int の3要素配列 int x[3][5]; // x は int の5要素配列 の3要素配列 int (*x[5])[3]; // x は int の3要素配列 へのポインタ の5要素配列 int (*x)(char); // x はcharを引数としてintを返す関数 // へのポインタ int (*[] x)(char); // x はcharを引数としてintを返す関数 // へのポインタ の配列
一行で複数のシンボルを宣言をする時は、 全ての変数が同じ型でなくてはなりません:
int x,y; // x と y は int int* x,y; // x と y は intへのポインタ int x,*y; // エラー, 2種類の型 int[] x,y; // x と y は intの配列 int x[],y; // エラー, 2種類の型
暗黙の型推論
AutoDeclaration: StorageClasses AutoDeclarationX ; AutoDeclarationX: Identifier = Initializer AutoDeclarationX , Identifier = Initializer
宣言が StorageClass で始まって、 型のわかる NonVoidInitializer が指定されているとき、 宣言の型は省略することが可能です。
static x = 3; // x はint型 auto y = 4u; // y はuint型 auto s = "string"; // s はchar[6]型 class C { ... } auto c = new C(); // c はクラスCのインスタンスへのハンドル
NonVoidInitializer に前方参照を含むことはできません (この制限は将来的に取り除かれる予定です)。 暗黙に推論される型は、 実行時ではなくコンパイル時に静的に決まります。
型定義
typedef によって、「強い型」を導入できます。強い型とは、型システムによって、 関数オーバーロードやデバッガに対して、オリジナルの型と厳密に区別される型です。
typedef int myint; void foo(int x) { . } void foo(myint m) { . } . myint b; foo(b); // foo(myint) が呼ばれる元の型とは違う初期値を typedef で指定することができます:
typedef int myint = 7; myint m; // 7 に初期化される
型の別名
型への別名を使うと便利なことがしばしばあります。 例えば、関数へのポインタのような長い複雑な型を記述するときなど。 Dでは、alias 宣言によってこれを実現します。
alias abc.Foo.bar myint;
別名となっている型は、意味論的には元の型と全く同一に扱われます。 デバッガは二つの型を区別しませんし、関数オーバーロードの際も、 違いは生じません。例として:
alias int myint; void foo(int x) { . } void foo(myint m) { . } // エラー。同じ関数fooが二回以上定義されている
型のaliasは、Cのtypedefと同等です。
別名宣言
型だけでなく、任意のシンボルに対する alias を宣言できます。 例:
import string; alias string.strlen mylen; ... int len = mylen("hello"); // 実際には string.strlen() が呼ばれる
次のような別名宣言が全て有効です:
template Foo2(T) { alias T t; } alias Foo2!(int) t1; alias Foo2!(int).t t2; alias t1.t t3; alias t2 t4; t1.t v1; // v1 はint型 t2 v2; // v2 はint型 t3 v3; // v3 はint型 t4 v4; // v4 はint型
シンボルの別名は、 長いqualifyされた名前を省略したり、 シンボルから別のシンボルへ転送する方法として役に立ちます:
version (Win32) { alias win32.foo myfoo; } version (linux) { alias linux.bar myfoo; }
シンボルを現在のスコープへ 'import' するのに alias を利用できます:
alias string.strlen strlen;
alias によってオーバーロードされた関数達をまとめて 'import' することも可能で、 その際には、新しく導入される関数は、現在のスコープに存在した関数とさらにオーバーロードされます:
class A { int foo(int a) { return 1; } } class B : A { int foo( int a, uint b ) { return 2; } } class C : B { int foo( int a ) { return 3; } alias B.foo foo; } class D : C { } void test() { D b = new D(); int i; i = b.foo(1, 2u); // B.foo が呼ばれる i = b.foo(1); // C.foo が呼ばれる }
注: 型のaliasは、見かけだけではシンボルの alias 宣言と区別できないことがあります:
alias foo.bar abc; // 型? シンボル?
この区別は意味解析のフェーズで行われます。
aliasを式に対して使うことはできません:
struct S { static int i; } S s; alias s.i a; // 不正。s.i は式 alias S.i b; // ok b = 4; // S.i を 4 にする
extern宣言
記憶クラス extern のつきで宣言された変数の領域は、 モジュール内には確保されません。 同じ名前で変数を定義した別のオブジェクトファイルを リンクする必要があります。 この機能の使いどころは、 C言語のグローバル変数宣言の利用です。typeof
Typeof: typeof ( Expression ) typeof ( return )
Typeof は、式の型に基づいて型を指定する方法です。 例えば:
void func(int i) { typeof(i) j; // j の型はint typeof(3 + 6.0) x; // x の型はdouble typeof(1)* p; // p の型はintへのポインタ int[typeof(p)] a; // a の型はint[int*] writefln("%d", typeof('c').sizeof); //1を表示 double c = cast(typeof(1.0))j; // jをdoubleへキャスト }
Expression は評価されず、 その型情報のみが生成されます:
void func() { int i = 1; typeof(++i) j; // j はintと宣言される。iは増加しない。 writefln("%d", i); // 1と表示 }
二つほど、特別な場合があります:
- typeof(this) は、例えメンバ関数の外であっても、 非静的メンバ関数の中で this が指すはずの型になることがあります。
- 同様に、typeof(super) は非静的メンバ関数の中で super が指すであろう型になることがあります。
class A { } class B : A { typeof(this) x; // x は B と宣言される typeof(super) y; // y は A と宣言される } struct C { typeof(this) z; // z は C* と宣言される typeof(super) q; // エラー。構造体 C には super がない } typeof(this) r; // エラー。構造体やクラス宣言の内部でない
Typeof がもっとも有効なのは、 templateを使ったジェネリックなコードを書く時です。
void 初期化子
VoidInitializer: void通常、変数は明示的な Initializer で初期化されるか、 その型のデフォルトの値がセットされます。 しかし、Initializer が void であった場合に限り、 変数は初期化されません。そのような変数が 値をセットされる前に使用されると、未定義動作を引き起こすことがあります。
void foo() { int x = void; writefln(x); // 意味のない値を表示 }ですから、void 初期化子は速度が最優先のコードを最適化する最後の手段としておくべきです。