宣言
文法
宣言は 、DeclDefに含まれるため、関数本体の内部でDeclarationStatementとして使用できるだけでなく、 関数の外部でも使用できる。
Declaration: FuncDeclaration VarDeclarations AliasDeclaration AliasAssign AggregateDeclaration EnumDeclaration ImportDeclaration ConditionalDeclaration StaticForeachDeclaration StaticAssert TemplateDeclaration TemplateMixinDeclaration TemplateMixin
集約
AggregateDeclaration: ClassDeclaration InterfaceDeclaration StructDeclaration UnionDeclaration
変数宣言
VarDeclarations: StorageClassesopt BasicType TypeSuffixesopt IdentifierInitializers ; AutoDeclaration IdentifierInitializers: IdentifierInitializer IdentifierInitializer , IdentifierInitializers IdentifierInitializer: Identifier Identifier TemplateParametersopt = Initializer Declarator: TypeSuffixesopt Identifier
参照:
ストレージクラス
「型クラスとストレージクラス」を参照。
StorageClasses: StorageClass StorageClass StorageClasses StorageClass: LinkageAttribute AlignAttribute AtAttribute deprecated enum static extern abstract final override synchronized auto scope const immutable inout shared __gshared Property nothrow pure ref
宣言構文
宣言構文は通常、配列も含めて右から左に読む。
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は3 intの静的配列である int[3][5] x; // xは3つのintからなる5つの静的配列 int[3]*[5] x; // xは3 intの静的配列へのポインタ5個からなる静的配列
ポインタ、配列 およびTypeSuffixを参照。
関数ポインタ
関数ポインタは、function キーワードを使用して宣言される。
int function(char) x; // xはcharを引数にとり // intを返す関数 // へのポインタである int function(char)[] x; // xはcharを引数にとり // intを返す関数 // へのポインタの配列である //
C言語のスタイルでの宣言
C言語のスタイルによる配列、関数ポインタ、および配列へのポインタの宣言は サポートされていない。以下のC言語の宣言は比較のみを目的としている。
int x[3]; // C 3つのintの静的配列 int x[3][5]; // C 5つのintの配列3つからなる静的配列 int (*x[5])[3]; // C 3つのintの静的配列への5つのポインタの静的配列 int (*x)(char); // C char引数を受け取り、intを // 返す関数へのポインタ int (*[] x)(char); // C char引数を取り、intを // 返す関数へのポインタの配列
- D型では右から左に読み進めるだけでよいが、 Cでは括弧が必要な場合があり、 時計回り/らせん規則を使用して反復的に型を読み進める必要がある。
- C関数ポインタ宣言の場合、a (*b)(c); 、Cパーサーは それを明確に解析するために型検索を試みる必要がある。 a という関数への呼び出しであり、関数ポインタを返し、 すぐに呼び出される可能性がある。D関数ポインタ構文は 明確であり、型を前方宣言する必要がない。
複数のシンボルの宣言
複数のシンボルを宣言する宣言では、すべての宣言は 同じ型でなければならない。
int x, y; // xとyはintである int* x, y; // xとyはintへのポインタである int[] x, y; // xとyはintの配列である
これはC言語とは対照的である。
int x, *y; // xはint、yはintへのポインタである int x[], y; // xは配列/ポインタ、yはintである
初期化
Initializer: VoidInitializer NonVoidInitializer NonVoidInitializer: AssignExpression ArrayLiteral StructInitializer
初期化子が指定されない場合、変数は その型のデフォルト.init 値に 設定される。
変数は、NonVoidInitializerで初期化することができる。
参照:配列の初期化。
無効な初期化
VoidInitializer: void
通常、変数は初期化される。 しかし、変数の初期化子がvoid の場合、変数は初期化されない。 安全でない値を含む可能性がある型(ポインタ型など)の変数に対するvoid初期化子は、@safe コードでは許可されない。
void bad() { int x = void; writeln(x); // 実装定義値を表示する }voidで初期化された変数の値が 設定される前に使用され、その値が参照、ポインタ、または不変の構造体のインスタンスである場合、 動作は未定義である。 xml-ph-00
void muchWorse() { char[] p = void; writeln(p); // 黙示録が起こるかもしれない }
- スタティック配列がスタック上にある場合、Void初期化子は有用であるが、 一時バッファのように部分的にしか使用できない場合もある。 Void初期化子はコードの高速化につながる可能性があるが、配列要素が読み込まれる前に必ず設定されるようにしなければならないため、リスクも伴う 。
- これは構造体についても同様である。
- 個々のローカル変数に対して void 初期化子を使用することはほとんど役に立たない。 最新の最適化機能では、後で初期化される場合は、初期化の不要な保存領域が削除されるからだ。
- ホットコードパスについては、void初期化子が実際に結果を改善するかどうかを確認するために、プロファイリングを行う価値がある。
暗黙の型推論
AutoDeclaration: StorageClasses AutoAssignments ; AutoAssignments: AutoAssignment AutoAssignments , AutoAssignment AutoAssignment: Identifier TemplateParametersopt = Initializer
宣言がStorageClassで始まり、 型が推論可能なNonVoidInitializerを持つ場合、 宣言の型は省略できる。
static x = 3; // xはint型である auto y = 4u; // yはuint型である auto s = "Apollo"; // sはimmutable(char)[]型、すなわち文字列である class C { ... } auto c = new C(); // cはクラスCのインスタンスへのハンドルである
NonVoidInitializerには前方参照を含めることはできない (将来的にこの制限が撤廃される可能性もある)。 暗黙的に推論された型は、 実行時ではなくコンパイル時に宣言に静的にバインドされる。
ArrayLiteralは、 静的配列ではなく動的配列の型と推論される。
auto v = ["resistance", "is", "useless"]; // 型はstring[]であり、string[3]ではない
グローバルおよび静的初期化子
グローバル変数または静的変数の初期化子は、 コンパイル時に評価可能でなければならない。 実行時の初期化は静的コンストラクタによって行われる。
- いくつかのポインタが他の関数やデータのアドレスで初期化できるかどうか 。
エイリアス宣言
AliasDeclaration: alias StorageClassesopt BasicType TypeSuffixesopt Identifiers ; alias StorageClassesopt BasicType FuncDeclarator ; alias AliasAssignments ; Identifiers: Identifier Identifier , Identifiers AliasAssignments: AliasAssignment AliasAssignments , AliasAssignment AliasAssignment: Identifier TemplateParametersopt = StorageClassesopt Type Identifier TemplateParametersopt = FunctionLiteral Identifier TemplateParametersopt = StorageClassesopt Type Parameters MemberFunctionAttributesopt
エイリアス宣言は、型または別のシンボルを参照するシンボル名を作成する。 その名前は、対象が現れる可能性がある場所であればどこでも使用できる。 エイリアス化できるものは以下のとおりである。
型エイリアス
alias myint = abc.Foo.bar;
エイリアス型は、エイリアスされた型と意味的には同一である。 デバッガーは両者を区別することができず、関数オーバーロードに関しては違いはない。 例えば:
alias myint = int; void foo(int x) { ... } void foo(myint m) { ... } // エラー、多重定義関数fooである
型エイリアスは、他のシンボルエイリアスと区別がつかないように見えることがある。
alias abc = foo.bar; // 型なのかシンボルなのか?
シンボルエイリアス
シンボルは、別のシンボルのエイリアスとして宣言することができる。 例:
import planets; alias myAlbedo = planets.albedo; ... int len = myAlbedo("Saturn"); // 実際にはplanets.albedo()を呼び出している
以下のエイリアス宣言は有効である。
template Foo2(T) { alias t = T; } alias t1 = Foo2!(int); alias t2 = Foo2!(int).t; alias t3 = t1.t; alias t4 = t2; t1.t v1; // v1はint型である t2 v2; // v2はint型である t3 v3; // v3はint型である t4 v4; // v4はint型である
エイリアス付きのシンボルは、長い修飾シンボル名の省略形として、あるいは あるシンボルへの参照を別のシンボルにリダイレクトする方法として有用である 。
version (Win32) { alias myfoo = win32.foo; } version (linux) { alias myfoo = linux.bar; }
エイリアスは、 インポートされたモジュールまたはパッケージからシンボルを「インポート」するために使用できる。 現在のスコープにインポートする:
static import string; ... alias strlen = string.strlen;
オーバーロードセットのエイリアス
エイリアスは、現在のスコープで関数でオーバーロードされているオーバーロード関数のセットを「インポート」することもできる。
class B { int foo(int a, uint b) { return 2; } } class C : B { // オーバーロードを宣言すると、ベースクラスのオーバーロードが隠蔽される int foo(int a) { return 3; } // 隠されたオーバーロードを再宣言する alias foo = B.foo; } void main() { import std.stdio; C c = new C(); c.foo(1, 2u).writeln; // B.fooを呼び出す c.foo(1).writeln; // C.fooを呼び出す }
変数のエイリアス
変数はエイリアス化できるが、"式"はできない。
int i = 0; alias a = i; // OK alias b = a; // alias a variable alias a++; b++; assert(i == 2); //alias c = i * 2; // エラー //alias d = i + i; // エラー
集約のメンバはエイリアス化できるが、非静的 フィールドのエイリアスは親の型外からはアクセスできない。
struct S { static int i = 0; int j; alias a = j; // OK void inc() { a++; } } alias a = S.i; // OK a++; assert(S.i == 1); alias b = S.j; // 許可 static assert(b.offsetof == 0); //b++; // エラー、Sのインスタンスがない //S.a++; // エラー、Sのインスタンスがない S s = S(5); s.inc(); assert(s.j == 6); //alias c = s.j; // 廃止予定
関数型のエイリアス
関数型にはエイリアスを付けることができる。
alias Fun = int(string); int fun(string) {return 0;} static assert(is(typeof(fun) == Fun)); alias MemberFun1 = int() const; alias MemberFun2 = const int(); // 先頭の属性は戻り値型ではなくfuncに適用される static assert(is(MemberFun1 == MemberFun2));
型エイリアスを使用して、異なるデフォルト引数を持つ関数を呼び出したり、 必須引数をデフォルト引数に変更したり、その逆に変更したりすることができる。
import std.stdio : writeln; void fun(int v = 6) { writeln("v: ", v); } void main() { fun(); // v: 6を表示する alias Foo = void function(int=7); Foo foo = &fun; foo(); // v: 7を表示する foo(8); // v: 8を表示する }
import std.stdio : writefln; void main() { fun(4); // a: 4, b: 6, c: 7を表示する Bar bar = &fun; //bar(4); // コンパイルエラー、`Bar`エイリアスは // 明示的な第2引数を必要とするため bar(4, 5); // a: 4, b: 5, c: 9を表示する bar(4, 5, 6); // a: 4, b: 5, c: 6を表示する Baz baz = &fun; baz(); // a: 2, b: 3, c: 4を表示する } alias Bar = void function(int, int, int=9); alias Baz = void function(int=2, int=3, int=4); void fun(int a, int b = 6, int c = 7) { writefln("a: %d, b: %d, c: %d", a, b, c); }
エイリアス割り当て
AliasAssign: Identifier = Type
エイリアス宣言(AliasDeclaration)には、エイリアス割り当て(AliasAssign)で新しい値を割り当てることができる。
template Gorgon(T) { alias A = long; A = T; // Aに新しい値を代入する alias Gorgon = A; } pragma(msg, Gorgon!int); // intを表示する
- AliasAssignと対応するAliasDeclaration は、両方とも 同じTemplateDeclaration で宣言されていなければならない。
- 対応するAliasDeclarationは、AliasAssignの前に"語彙上"現れなければならない。
- 対応するAliasDeclarationは、オーバーロードされたシンボルを参照してはならない。
- エイリアス宣言の値またはエイリアス代入の左辺(l値)は、 同じl値に対する別のエイリアス代入 の右辺以外では使用できない。
import std.meta : AliasSeq; static if (0) // 比較のための再帰メソッド { template Reverse(T...) { static if (T.length == 0) alias Reverse = AliasSeq!(); else alias Reverse = AliasSeq!(Reverse!(T[1 .. T.length]), T[0]); } } else // テンプレートのインスタンス化を最小化する反復メソッド { template Reverse(T...) { alias A = AliasSeq!(); static foreach (t; T) A = AliasSeq!(t, A); // 別名 Assign alias Reverse = A; } } enum X = 3; alias TK = Reverse!(int, const uint, X); pragma(msg, TK); // tuple(3, (const(uint)), (int))を表示する
エイリアスの再割り当て
AliasReassignment: Identifier = StorageClassesopt Type Identifier = FunctionLiteral Identifier = StorageClassesopt BasicType Parameters MemberFunctionAttributesopt
テンプレート内のエイリアス宣言は、新しい値に再代入することができる。
import std.meta : AliasSeq; template staticMap(alias F, Args...) { alias A = AliasSeq!(); static foreach (Arg; Args) A = AliasSeq!(A, F!Arg); // 別名の再割り当て alias staticMap = A; } enum size(T) = T.sizeof; static assert(staticMap!(size, char, wchar, dchar) == AliasSeq!(1, 2, 4));
識別子は、語彙的に先行するAliasDeclaration に解決されなければならない。 両者は同じ TemplateDeclaration のメンバーでなければならない。
AliasReassignmentの右辺は、AliasDeclarationの右辺を置き換える。
AliasDeclaration が AliasReassignment の右辺以外で参照された場合、 AliasDeclarationは再代入できなくなる。
外部宣言
extern の記憶クラスを持つ変数宣言は、 モジュール内に記憶領域を割り当てない。これらは、他のオブジェクトファイルで定義する必要があり、 一致する名前をリンクする必要がある。
extern 宣言には、オプションでexternのリンク属性を続けることができる。 リンク属性がない場合は、 デフォルトでextern(D) となる。
// このモジュール内でCで割り当てられ、初期化された変数 extern(C) int foo; // Cリンケージでこのモジュール外に割り当てられた変数 // (静的にリンクされたCライブラリや別のモジュールなど) extern extern(C) int bar;
- 外部宣言の主な利点は、 CまたはC++ファイルのグローバル変数宣言および関数と接続することである。
型修飾子とストレージクラス
型修飾子は、既存の基本型から派生型を作成するもので あり、作成された型は、その型の複数のインスタンスを作成するために使用できる。
例えば、immutable という型修飾子は、 "不変の型"の変数を作成するために使用できる。
immutable(int) x; // typeof(x) == immutable(int) immutable(int)[] y; // typeof(y) == immutable(int)[] // typeof(y[0]) == immutable(int) // 型コンストラクタはエイリアスが可能な新しい型を生成する: alias ImmutableInt = immutable(int); ImmutableInt z; // typeof(z) == immutable(int)
一方、ストレージクラスは新しい型を作成するのではなく、 変数や宣言中の関数で使用されるストレージの種類のみを記述する。 例えば、メンバ関数はconst ストレージクラスで宣言することで、暗黙的なthis 引数を変更しないことを示すことができる
struct S { int x; int method() const { //x++; // エラー: このメソッドはconstであり、this.xを変更できない return x; // OK: まだthis.xを読むことができる。 } }
いくつかのキーワードは 型修飾子とストレージクラスの両方として使用できるが、ref のように、新しい型の構築に使用できないストレージクラスもある。
ref ストレージクラス
ref で宣言されたパラメータは 参照渡しされる。
void func(ref int i) { i++; // iの変更は呼び出し側で見ることができる。 } void main() { auto x = 1; func(x); assert(x == 2); // しかし、refは型修飾子ではないので、以下は不正である: //ref(int) y; // エラー: refは型修飾子ではない。 }
関数もref として宣言することができ、 その場合は戻り値が参照渡しされることを意味する。
ref int func2() { static int y = 0; return y; } void main() { func2() = 2; // func2()の返却値は変更できる。 assert(func2() == 2); // しかし、func2()によって返された参照は、変数に伝搬しない。 // なぜなら、'ref'は返却値自体にのみ適用され、 // それ以降に生成される変数には適用されないからである: auto x = func2(); static assert(is(typeof(x) == int)); // N.B.: *not* ref(int); // ref(int)という型は存在しない。 x++; assert(x == 3); assert(func2() == 2); // xxは、func2()が返したものへの参照ではない; // func2()からrefストレージクラスを継承していない。 }
修飾された型を返すメソッド
const などのキーワードは、 型修飾子としても、またストレージクラスとしても使用できる。 この区別は、キーワードが使用される構文によって決定される。
struct S { /* ここでのconstは型修飾子なのか、それとも格納クラスなのか? * 戻り値はconst(int)なのか、それとも * (変更可能な)intを返すconst関数なのか? */ const int* func() // const関数である。 { //++p; // エラー、this.pはconstである //return p; // エラー、const(int)*をint*に変換できない return null; } const(int)* func() // const intへのポインタを返す関数 { ++p; // OK、this.pは変更可能 return p; // OK、int*は暗黙のうちにconst(int)*に変換できる } int* p; }
struct S { // ここで'const'が返却型に適用されることは明らかである: const(int) func1() { return 1; } // そして、ここでの'const'が関数に適用されることは明らかである: int func2() const { return 1; } }
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.109.1
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku