式
式
Expression: CommaExpression
式とは、演算子とオペランドのシーケンスで、評価を指定するものである。 式の構文、評価の順序、および意味は以下の通りである。
式は、結果の型を持つ値を計算するために使用される。 これらの値は、その後、割り当て、 テスト、または無視することができる。式には副作用がある場合もある。
定義と用語
完全な式
任意の式 expr の完全な式は、以下のように定義される。expr が 他の式expr1 の部分式として解析される場合、expr の完全な式は expr の完全な式任意の式 expr について、expr の完全な式は以下のように定義される。expr が 他の式expr1 の部分式として解析される場合、expr の完全な式は expr1 の完全な式となる。それ以外の場合、exprはそれ自身の完全な式となる。
各式には、それぞれ独自の完全な式がある。 例:
return f() + g() * 2;
上のg() * 2 の完全な式はf() + g() * 2 であるが、f() + g() の完全な式ではない。なぜなら、後者は部分式として解析されないからである。
注釈: 定義は単純明快だが、関数リテラルにはいくつかの微妙な点がある。
return (() => x + f())() * g();
上のf() の完全な式はx + f() であり、return に渡される式ではない。 これは、x + f() の親が関数リテラル型であり、式型ではないためである。
lvalue
以下の式、およびそれ以外は、lvalue式またはlvalueと呼ばれる。
xml-ph-0000@deepl.internalおよびxml-ph-0001@deepl.internalのメンバ関数内にあるもの。- thisstruct およびunion のメンバ関数内、
- 参照を返す変数、関数名、または関数の呼び出し、
- . PostfixExpressionおよび モジュールスコープ演算子 の、ドットの右辺が変数、フィールド(直接またはstatic )、関数名、または参照を返す関数の呼び出しである場合の
- 以下の式の結果:
- 組み込みの単項演算子 + ("l値"に適用した場合)、* 、++ (プレフィックスのみ)、-- (プレフィックスのみ)
- 組み込みのインデックス演算子 [] (ただしスライス演算子は除く)、
- 組み込みの代入演算子、すなわち、= 、+= 、*= 、/= 、%= 、&= 、|= 、^= 、~= 、<<= 、>>= 、>>>= 、^^= 。
- ユーザー定義の演算子 は、値下げの結果として呼び出された関数が 参照によって返す場合のみ、
- 以下の状況下で、ConditionalExpression演 算子 e ? e1 : e2
- e 1とe2が同じ型の"l値"である場合、または
- e1とe2の どちらかが T 型のl値であり、もう一方がalias this を持ち、T 型のl値に変換される場合、
- mixin 式は、 mixin への引数のコンパイルの結果として得られる式が l値である場合にのみ、l値となる。
- cast(U) T 型のl値に適用される式 で、 が に暗黙的に変換できる場合、T* U*
- cast(TypeCtorsopt)l値に適用された場合。
r値
l値ではない式はr値である。r値には、すべてのリテラル、__FILE__ や__LINE__ などの特別な値キーワード、enum 値、および上記でl値として定義されていない式の結果が含まれる。
組み込みのアドレス・オブ・オペレータ(単項演算子& )は、l値にのみ適用できる。
最小の短絡式
fullexprという完全な式の部分式である式expr が与えられた場合、 最小のショートサーキット式は 、fullexprの AndAndExpression(&&) または OrOrExpression(||)であり、かつ、expr がscexprの部分式であるような、fullexprの最短の部分式scexprである。 例:
((f() * 2 && g()) + 1) || h()上の部分式f() * 2 の最小の短絡式はf() * 2 && g() である。例:
(f() && g()) + h()上の部分式h() には最小の短絡式はない。
評価の順序
組み込みの接頭辞単項式++ および-- は、以下のように代入にダウンス(書き換え)されたかのように評価される。
Expression | Equivalent |
---|---|
++expr | ((expr) += 1) |
--expr | ((expr) -= 1) |
したがって、接頭辞++ と-- の結果は、副作用が実行された後の"l値"となる。
組み込みのポストフィックス単項式++ と-- は、以下のように、ラムダ呼び出しに変換(書き換え)されたかのように評価される。
Expression | Equivalent |
---|---|
expr++ | (ref x){auto t = x; ++x; return t;}(expr) |
expr-- | (ref x){auto t = x; --x; return t;}(expr) |
したがって、postfix++ と-- の結果は、副作用が発生する直前に"r値"となる。
AssignExpression 、OrOrExpression、AndAndExpression以外の2項式は、 語彙順(左から右)で評価される。 例:xml-ph-0000@deepl.internal
int i = 2; i = ++i * i++ + i; assert(i == 3 * 3 + 4);
OrOrExpressionおよびAndAndExpressionは、まず左辺の引数を評価する。 次に、OrOrExpressionは、左辺がゼロ以外を評価しない場合にのみ、右辺を評価する。 AndAndExpressionは、左辺がゼロ以外を評価する場合にのみ、右辺を評価する。
ConditionalExpression はまず左辺の引数を評価する。 次に、その結果がゼロ以外の場合、2番目のオペランドが評価される。そうでない場合は、3番目のオペランドが 評価される。
extern(D) リンク(これは デフォルトのリンク)を持つ関数への呼び出しは、以下の順序で評価される。まず、必要に応じて、呼び出す関数のアドレスが 評価される(例えば、計算された関数ポインタやデリゲートのケース)。次に、 引数が左から右の順に評価される。最後に、転送が関数に渡される。例:
void function(int a, int b, int c) fun() { writeln("fun() called"); static void r(int a, int b, int c) { writeln("callee called"); } return &r; } int f1() { writeln("f1() called"); return 1; } int f2() { writeln("f2() called"); return 2; } int f3(int x) { writeln("f3() called"); return x + 3; } int f4() { writeln("f4() called"); return 4; } // fun()が評価され、次にf1()が評価され、次にf2()が評価され、次にf3()が評価され、次にf4()が評価される // その後、制御は呼び出し側に移される fun()(f1(), f3(f2()), f4());
- AssignExpressionのオペランドの評価順序。
- extern (D) 以外のリンクを持つ関数の関数引数の評価順序。
一時的なものの寿命
式や文は、r値を生成したり、r値を消費したりすることがある。そのような値は 一時値と呼ばれ、名前を持たず、可視スコープを持たない。その寿命は、 このセクションで定義されているように、自動的に管理される。
一時的な値を生成する評価のそれぞれについて、その一時的な値の有効期間はその評価時点から開始される。 これは、式で初期化された通常の名前付き値の生成と同様である。
一時変数の有効期間の終了は、通常のスコープのルールに従わず、 以下のように定義される。
- もし:
- 式全体が最も短いショートカット式exprであり、
- 一時変数は、&& または|| 演算子の右辺で作成され、
- 右辺が評価され、
- その他のすべてのケースでは、関数を呼び出す目的で生成された一時オブジェクトは、 式全体の末尾まで遅延される。 破棄の順序は、生成の順序と逆である。
式のサブ式が例外をスローした場合、そのサブ式の評価までに作成されたすべてのテンポラリは、 上記の規則に従って破棄される。 まだ構築されていないテンポラリに対しては、デストラクタの呼び出しは発行されない。
注釈: これらの規則の背景にある考え方は、一時変数のデストラクタは完全な式の最後に、 構築の逆順で遅延されるというものである。ただし、&& と|| の右辺は、より大きな式の一部であっても、それ自体が完全な式として扱われる。
注釈:ConditionalExpression e1 ? e2 : e3は、 条件付きで式を評価する特殊なケースではない。e1と e2 および e3 のいずれかによって一時オブジェクトが作成される可能性がある。それらのデストラクタは、 作成された順序とは逆の順序で完全な式の最後に挿入される。
例:
import std.stdio; struct S { int x; this(int n) { x = n; writefln("S(%s)", x); } ~this() { writefln("~S(%s)", x); } } void main() { bool b = (S(1) == S(2) || S(3) != S(4)) && S(5) == S(6); }
S(1) S(2) S(3) S(4) ~S(4) ~S(3) S(5) S(6) ~S(6) ~S(5) ~S(2) ~S(1)まず、S(1) とS(2) は語彙順に評価される。ルールに従って、これらは 式全体が終了した時点で、逆順に破棄される。S(1) == S(2) の比較はfalse を生成するため、|| の右辺が評価され、S(3) とS(4) が評価される。 これも語彙順である。しかし、これらの破棄は式全体の終了まで延期されない 。代わりに、S(4) 、次にS(3) は、|| 式の終了時に破棄される。 破棄後、S(5) とS(6) は語彙順に構築される。これらもまた 式の終了時に破棄されるのではなく、&& 式の終了時に破棄される。 したがって、S(6) とS(5) の破棄は、S(2) とS(1) の破棄よりも先に実行される。
カンマ式
CommaExpression: AssignExpression CommaExpression , AssignExpression
, の左オペランドが評価され、次に右オペランドが評価される。 式の型は右オペランドの型であり、結果は右オペランドの結果である。 カンマ式の結果を使用することはできない。
代入式
AssignExpression: ConditionalExpression ConditionalExpression = AssignExpression ConditionalExpression += AssignExpression ConditionalExpression -= AssignExpression ConditionalExpression *= AssignExpression ConditionalExpression /= AssignExpression ConditionalExpression %= AssignExpression ConditionalExpression &= AssignExpression ConditionalExpression |= AssignExpression ConditionalExpression ^= AssignExpression ConditionalExpression ~= AssignExpression ConditionalExpression <<= AssignExpression ConditionalExpression >>= AssignExpression ConditionalExpression >>>= AssignExpression ConditionalExpression ^^= AssignExpression
すべての代入式において、左オペランドは変更可能な 左辺値でなければならない。代入式の型は左オペランドの型であり、 結果は代入が行われた後の左オペランドの値である。 結果の式は変更可能な左辺値である。
- オペランドの記憶領域が部分的に重複している
- オペランドの記憶領域が完全に重複しているが、型が異なる場合
- オペランドの記憶域が部分的に重複している
- オペランドの記憶域が完全に重複しているが、型が異なる場合
単純代入式
演算子が= の場合、単純代入である。
- 左オペランドが構造体で、opAssign を定義している場合、 その動作はオーバーロード関数によって定義される。
- 左オペランドと右オペランドが同じ構造体型であり、かつ構造体 型が Postblit を持つ場合、コピー操作は 構造体 Postblit で説明されているとおりとなる。
- l値が動的配列の.length プロパティである場合、動作は 「動的配列の長さの設定」で説明されているとおりである。
- l値が静的配列またはスライスである場合、動作は 「配列のコピーと 配列の設定」で説明されているとおりである。
- l値がユーザー定義のプロパティの場合、動作は 「プロパティ関数」で説明されているとおりである。
そうでない場合は、右オペランドは左オペランドの型に暗黙的に変換され、 それに代入される。
代入演算子式
組み込み型の引数に対する代入演算子式は、
a op= b意味的には以下と同等である。
a = cast(typeof(a))(a op b)ただし、
- オペランドa は一度だけ評価される。
- オーバーロード演 算子は= とは異なる関数を使用し、
- >>>= の左オペランドはシフト前に整数昇格を行わない。
ユーザー定義型の場合、代入演算子式は 二項演算子とは別にオーバーロードされる。 それでも左オペランドはl値でなければならない。
条件式
ConditionalExpression: OrOrExpression OrOrExpression ? Expression : ConditionalExpression
最初の式はbool に変換され、評価される。
true である場合、2番目の式が評価され、 その結果が条件式の結果となる。
false であれば、3番目の式が評価され、 その結果が条件式の結果となる。
2番目または3番目の式がvoid 型である場合、 結果の型はvoid となる。それ以外の場合、2番目と3番目の 式は暗黙的に共通の型に変換され、これが 条件式の結果の型となる。
bool test; int a, b, c; ... test ? a = b : c = 2; // エラー (test ? a = b : c) = 2; // OK
これにより意図が明確になります。最初の文は、 次のコードと誤読されやすいからです。
test ? a = b : (c = 2);
論理式
OrOr式
OrOrExpression: AndAndExpression OrOrExpression || AndAndExpression
OrOrExpressionの結果の型はbool である。ただし、右オペランドがvoid 型の場合、結果はvoid 型となる。
OrOrExpressionは左オペランドを評価する。
左オペランドが型bool に変換され、true と評価される場合、 右オペランドは評価されない。OrOrExpressionの結果の型がbool の場合、 式の結果はtrue となる。
左オペランドがfalse の場合、右 オペランドが評価される。 OrOrExpressionの結果の型が bool の場合、 式の結果は右オペランドがbool 型に変換されたものとなる。
そしてそして式
AndAndExpression: OrExpression AndAndExpression && OrExpression
AndAndExpressionの結果の型はbool である。ただし、右オペランドがvoid 型の場合、結果はvoid 型となる。
AndAndExpressionは左オペランドを評価する。
左オペランドが型bool に変換され、false と評価される場合、 右オペランドは評価されない。AndAndExpressionの結果の型がbool の場合、 式の結果はfalse となる。
左オペランドがtrue の場合、右 オペランドが評価される。 AndAndExpressionの結果の型が bool の場合、式の結果は 右オペランドをbool 型に変換したものとなる。
ビット演算式
ビット単位の式は、 オペランドに対してビット単位の演算を実行する。 オペランドは整数型でなければならない。 まず、通常の算術変換が実行される。次に、ビット単位の 演算が実行される。
int x, a, b; x = a & 5 == b; // エラー x = a & 5 is b; // エラー x = a & 5 <= b; // エラー x = (a & 5) == b; // OK x = a & (5 == b); // OK
または式
OrExpression: XorExpression OrExpression | XorExpression
オペランドはORで結合される。
Xor式
XorExpression: AndExpression XorExpression ^ AndExpression
オペランドはXOR演算される。
そして、式
AndExpression: CmpExpression AndExpression & CmpExpression
オペランドはAND演算で結合される。
式を比較する
CmpExpression: EqualExpression IdentityExpression RelExpression InExpression ShiftExpression
等式の式
EqualExpression: ShiftExpression == ShiftExpression ShiftExpression != ShiftExpression
等式は、2つのオペランドを等しい(== )か、 または等しくない(!= )かで比較する。 結果の型はbool である。
不等号は、等号の論理否定として定義される。
オペランドが整数値の場合、 比較の前にそれらを共通の型に変換するために通常の算術変換が適用される。等価性は、 共通の型のビットパターンが完全に一致する
オペランドがポインタの場合、オペランドのビットパターンが完全に一致する場合に等価とみなされる。
float、double、および実数値の場合は、通常の算術変換が適用され、 比較前に共通の型に変換される。 値-0 と+0 は等しいとみなされる。 どちらか一方または両方のオペランドがNaNの場合、== はfalseを返し、!= はtrue を返す。 それ以外の場合、共通の型のビットパターンが等価であるか比較される。
静的配列および動的配列の場合、等価性は 配列の長さが 一致し、すべての要素が等しい 場合と定義される。
x.re == y.re && x.im == y.im
クラスと構造体の等価性
構造体オブジェクトの場合、等価性とは opEquals() メンバ関数の結果を意味する。 opEquals() が提供されていない場合、等価性は 対応するオブジェクトフィールドの等価性の 結果の論理積として定義される。
- 整列のずれがある場合
- フィールドにopEquals()
- NaNまたは-0 値を含む浮動小数点フィールドがある場合
クラスおよび構造体オブジェクトの場合、式(a == b)は 次のように書き換えられる。 a.opEquals(b) 、また、(a != b) は !a.opEquals(b) となる。
クラスオブジェクトの場合、== および!= 演算子はオブジェクトの内容を比較する目的で使用されるが、 これを機能させるには適切なopEquals オーバーライドを定義する必要がある。 ルートクラスであるObject が提供するデフォルトのopEquals は、 is 演算子(下記参照)と同等である。 null との比較は無効である。なぜなら、null には内容がないからだ。 代わりにis および!is 演算子を使用すること。
class C; C c; if (c == null) // エラー ... if (c is null) // OK ...
識別式
IdentityExpression: ShiftExpression is ShiftExpression ShiftExpression ! is ShiftExpression
is 演算子は、式の値の同一性を比較する。 非同一性を比較するには、e1 !is e2 を使用する。 結果の型はbool である。 被演算数は、比較前に共通の型にするために、通常の算術変換が行われる。
クラス/インターフェイスオブジェクトの場合、同一性はオブジェクト参照が同一であると定義される。 nullクラスオブジェクトはis と比較できる。 インターフェイスオブジェクトは、キャスト元のクラスと同じ参照を持つ必要はないことに注意。 あるinterface が別のinterface /class 値とクラスインスタンスを共有しているかどうかを調べるには、is と比較する前に両方のオペランドをObject にキャストする。
interface I { void g(); } interface I1 : I { void g1(); } interface I2 : I { void g2(); } interface J : I1, I2 { void h(); } class C : J { override void g() { } override void g1() { } override void g2() { } override void h() { } } void main() @safe { C c = new C; I i1 = cast(I1) c; I i2 = cast(I2) c; assert(i1 !is i2); // 同一ではない assert(c !is i2); // 同一ではない assert(cast(Object) i1 is cast(Object) i2); // 同一である }
構造体オブジェクトと浮動小数点値については、同一性は 演算子のビットが同一であると定義される。
静的配列と動的配列の場合、2つの配列の同一性は、 両方の配列が同じメモリ位置を参照し、同じ数の要素を含む 場合に与えられる。
Object o; assert(o is null); auto a = [1, 2]; assert(a is a[0..$]); assert(a !is a[0..1]); auto b = [1, 2]; assert(a !is b);
他のオペランド型については、同一性が等価であると定義される。
同一演算子is はオーバーロードできない。
関係式
RelExpression: ShiftExpression < ShiftExpression ShiftExpression <= ShiftExpression ShiftExpression > ShiftExpression ShiftExpression >= ShiftExpression
まず、演算子に対して通常の算術変換が実行される。 関係式の結果の型はbool である。
配列の比較
静的配列および動的配列の場合、CmpExpressionの結果は、 配列の最初の非等しい要素に適用された演算子の結果となる。 2つの配列が等しいと比較されるが、長さが異なる場合、 短い方の配列は長い方の配列よりも「短い」と比較される。
整数の比較
整数比較は、両方のオペランドが整数型である場合に発生する。
Operator | Relation |
---|---|
< | less |
> | より大きい |
<= | 以下 |
>= | 以上 |
== | 等しい |
!= | 等しくない |
< 、< 、= 、> 、または> 、= 式において、一方のオペランドが符号付きで、もう一方が符号なしであるのはエラーである。 キャストを使用して、両方のオペランドを符号付きにするか、両方のオペランドを符号なしにする。
浮動小数点数の比較
どちらか一方または両方のオペランドが浮動小数点の場合は、浮動小数点の 比較が行われる。
CmpExpressionには NaN 個のオペランドを指定できる。 オペランドのいずれか、または両方がNaN の場合、浮動小数点 比較演算は以下のように返される。
Operator | Relation | Returns |
---|---|---|
< | less | false |
> | greater | false |
<= | 以下 | false |
>= | 以上 | false |
== | 等しい | false |
!= | 順序なし、より小さい、またはより大きい | true |
クラスの比較
クラスオブジェクトの場合、EqualExpressionとRelExpressionは オブジェクトの内容を比較する。 したがって、null のクラス参照と比較することは無効である。なぜなら、null には内容がないからだ。
class C {} void fun() { C c; //if (c < null) {} // コンパイル時エラー assert(c is null); if (c > new C) {} // 実行時エラー }
クラスオブジェクトの場合、Object.opCmp() の結果が左オペランドとなり、0 の結果が右オペランドとなる。 EqualExpressionまたはRelExpression の結果(o1 op o2) は以下のとおりである。
(o1.opCmp(o2) op 0)emailemail
式では、
InExpression: ShiftExpression in ShiftExpression ShiftExpression ! in ShiftExpression
連想配列などのコンテナは、 特定のキーが含まれているかどうかをテストすることができる。
int foo[string]; ... if ("hello" in foo) { // 文字列が見つかった }
InExpressionの結果は、連想配列へのポインタとなる。 一致するキーがない場合、ポインタはnull となる。 一致するキーがある場合、ポインタはそのキーに関連付けられた値を指す。
!in 式は、in演算の論理否定である。
in 式は、関係式< 、<= などと同じ優先順位を持つ。
シフト式
ShiftExpression: AddExpression ShiftExpression << AddExpression ShiftExpression >> AddExpression ShiftExpression >>> AddExpression
オペランドは整数型でなければならず、整数拡張が行われる。 結果の型は、拡張後の左オペランドの型となる。 結果の値は、右オペランドの値によってビットがシフトされた結果となる。
- << は左シフト、
- >> は符号付き右シフト、
- >>> は符号なし右シフトである。
int c; int s = -3; auto y = c << s; // 実装定義値 auto x = c << 33; // エラー、許容される最大シフト数は31である
加法式
AddExpression: MulExpression AddExpression + MulExpression AddExpression - MulExpression AddExpression ~ MulExpression
加算式
加算演算の場合、+ および- :
オペランドが整数型の場合、それらは通常の算術変換を受け、 その後、通常の算術変換を使用して共通の型に変換される。
両方のオペランドが整数型であり、計算中にオーバーフローまたはアンダーフローが発生した場合は、 丸めが行われる。例えば:
- uint.max + 1 == uint.min
- uint.min - 1 == uint.max
- int.max + 1 == int.min
- int.min - 1 == int.max
どちらかのオペランドが浮動小数点数型の場合、もう一方は暗黙的に 浮動小数点数に変換され、通常の算術変換によって共通の型に変換される 。
浮動小数点演算子に対する加算式は結合されない。
ポインタの算術演算
最初のオペランドがポインタであり、2番目のオペランドが整数型である場合、 結果の型は最初のオペランドの型となり、結果の 値はポインタに2番目のオペランドを足したもの(または引いたもの)に、 最初のオペランドが指す型のサイズを掛けたものとなる。
int[] a = [1,2,3]; int* p = a.ptr; assert(*p == 1); *(p + 2) = 4; // `p[2] = 4`と同じ assert(a[2] == 4);
IndexOperationはポインタと共に使用することもでき、 整数を加算し、その結果を逆参照するのと同じ動作となる。
第2オペランドがポインタであり、第1オペランドが整数型の場合、 演算子が+ である場合、 オペランドは逆転され、前述のポインタ演算が適用される。
ポインタ算術によるポインタの生成はコードでは許可されていない @safe。
両方のオペランドがポインタであり、演算子が+ の場合、 それは不正である。
両方のオペランドがポインタであり、演算子が- の場合、 ポインタが減算され、 その結果がオペランドが指す型のサイズで割られる。 この計算では、void の想定されるサイズは1バイトである。 ポインタが異なる型を指している場合はエラーとなる。 結果の型はptrdiff_t である。
int[] a = [1,2,3]; ptrdiff_t d = &a[2] - a.ptr; assert(d == 2);
カテゴリ式
加算演算の場合、~ :
CatExpressionはコンテナのデータと他のデータを結合し、 新しいコンテナを生成する。
動的配列の場合、もう一方のオペランドは、別の配列であるか、または 配列の要素型に暗黙的に変換される単一の値でなければならない。 配列の結合を参照。
乗算式
MulExpression: UnaryExpression MulExpression * UnaryExpression MulExpression / UnaryExpression MulExpression % UnaryExpression
オペランドは算術型でなければならない。 これらは通常の算術変換を受ける。
整数オペランドの場合、* 、/ 、%は それぞれ、乗算、除算、剰余演算に対応する。 乗算の場合、オーバーフローは無視され、単に整数型に合うように切り捨てられる。
除算
/ および% 演算子の整数オペランドの場合、 商はゼロに丸められ、余りは 被除数と同じ符号となる。
次の被除数または剰余数:
- 分母が0
- 符号付きint.min が分子で、-1 が分母である
- long.min が分子、-1L が分母となる
コンパイル時実行中に検出された場合は、不正です。
浮動小数点
浮動小数点演算子については、* および/ の演算は IEEE 754 浮動小数点演算の同等演算に相当する。% は IEEE 754 の余りとは異なる。例えば、15.0 % 10.0 == 5.0 であるのに対し、 IEEE 754 ではremainder(15.0,10.0) == -5.0 となる。
浮動小数点オペランドの乗算式は結合性を持たない。
単項式
UnaryExpression: & UnaryExpression ++ UnaryExpression -- UnaryExpression * UnaryExpression - UnaryExpression + UnaryExpression ! UnaryExpression ComplementExpression DeleteExpression CastExpression ThrowExpression PowExpression
Operator | Description |
---|---|
& | l値のメモリアドレスを取得する -ポインタを参照 |
++ | 使用前にインクリメントする -評価順序を参照 |
-- | 使用前に減算する |
* | 参照/間接参照 - 通常はポインタに対して |
- | 負の |
+ | 正 |
! | 論理 NOT |
単項演算(- および+ )の前に、通常の整数プロモーションが実行される。
補数式
ComplementExpression: ~ UnaryExpression
ComplementExpressionsは整数型(bool を除く)で動作する。 値のすべてのビットが補数される。 補数演算の前に通常の整数昇格が行われる。
式を削除する
DeleteExpression: delete UnaryExpression
UnaryExpressionがクラスオブジェクト参照であり、 そのクラスにデストラクタが存在する場合、そのオブジェクトインスタンスに対してデストラクタが 呼び出される。
次に、UnaryExpressionがクラスオブジェクト参照、または 構造体インスタンスへのポインタであり、クラスまたは構造体が "delete" 演算子をオーバーロードしている場合、その演算子は クラスオブジェクトインスタンスまたは構造体インスタンスに対して呼び出される。
そうでない場合は、ガベージコレクタが呼び出され、 クラスインスタンスまたは構造体インスタンスに割り当てられたメモリが即座に解放される。
UnaryExpression がポインタまたは動的配列の場合、 ガベージコレクタが呼び出され、メモリが即座に解放される 。
ポインタ、動的配列、または参照は、削除が実行された後にnullに設定される。 削除後に別の参照を通じてデータを参照しようとすると、 "未定義の動作"となる。
UnaryExpressionがスタック上に割り当てられた変数である場合、 そのインスタンスに対してクラス・デストラクタ(存在する場合)が呼び出される。 ガベージ・コレクタは呼び出されない。
- delete を使用して、ガベージコレクタによって割り当てられていないメモリを解放する。
- delete のオペランドであったデータを参照する。
キャスト式
CastExpression: cast ( Type ) UnaryExpression CastQual
CastExpression は UnaryExpression を "型" に変換する。
cast(foo) -p; // (-p)をfoo型にキャストする (foo) - p; // fooからpを引く
基本データ型
基本型に対する暗黙の変換が 実行できない状況では、型システムはキャストを使用して メモリ領域の再解釈を受け入れることを強制される場合がある。
このような状況の例としては、より広い型をより狭い型に格納しようとする場合が挙げられる。
int a; byte b = a; // int型の式aを暗黙的にbyte型に変換することはできない
キャスト元の型がキャスト先の型よりも大きい場合、 値はキャスト先のサイズに合わせて切り捨てられる。
int a = 64389; // 00000000 00000000 11111011 10000101 byte b = cast(byte) a; // 10000101 ubyte c = cast(ubyte) a; // 10000101 short d = cast(short) a; // 11111011 10000101 ushort e = cast(ushort) a; // 11111011 10000101 writeln(b); writeln(c); writeln(d); writeln(e);
整数の型では、狭い型から広い型へのキャストは、 符号拡張を行うことで実行される。
ubyte a = 133; // 10000101 byte b = a; // 10000101 writeln(a); writeln(b); ushort c = a; // 00000000 10000101 short d = b; // 11111111 10000101 writeln(c); writeln(d);
クラス参照
クラス参照を派生クラス参照にキャストする場合は、 実行時にチェックを行い、それが 本当にダウンキャストであることを確認する。そうでない場合は、null が結果として返される。
class A {} class B : A {} void main() { A a = new A; //B b = a; // エラー、キャストが必要 B b = cast(B) a; // aがBでない場合、bはnullである assert(b is null); a = b; // キャストは必要ない a = cast(A) b; // アップキャストの実行時チェックは必要ない assert(a is b); }
B オブジェクトo がクラスのインスタンスであるかどうかを判断するには 、キャストを使用する。
if (cast(B) o) { // oはBのインスタンスである } else { // oはBのインスタンスではない }
ポインタ型をクラス型にキャストしたり、クラス型をポインタ型にキャストしたりするには、型ペイント( すなわち再解釈キャスト)として行う。
ポインタ
ポインタ変数を別のポインタ型にキャストすると、 ポインタの参照解除の結果として取得される値が変更され、ポインタ演算が実行されるバイト数も 変更される。
int val = 25185; // 00000000 00000000 01100010 01100001 char *ch = cast(char*)(&val); writeln(*ch); // a writeln(cast(int)(*ch)); // 97 writeln(*(ch + 1)); // b writeln(cast(int)(*(ch + 1))); // 98
同様に、動的に割り当てられた配列をより小さいサイズの型にキャストする場合、 初期の配列のバイトは、新しい次元に従って分割され、再グループ化される。
import core.stdc.stdlib; int *p = cast(int*) malloc(5 * int.sizeof); for (int i = 0; i < 5; i++) { p[i] = i + 'a'; } // p = [97, 98, 99, 100, 101] char* c = cast(char*) p; // c = [97, 0, 0, 0, 98, 0, 0, 0, 99 ...] for (int i = 0; i < 5 * int.sizeof; i++) { writeln(c[i]); }
A型のポインタをB型のポインタにキャストし、B型がA型よりも広い場合、 Aのサイズを超えるメモリにアクセスしようとすると、動作が未定義となる。
char c = 'a'; int *p = cast(int*) (&c); writeln(*p);
また、基本データ型へのポインタのキャストも可能である。 一般的な方法としては、ポインタをint値にキャストし、 そのアドレスを表示する、というものがある。
import core.stdc.stdlib; int *p = cast(int*) malloc(int.sizeof); int a = cast(int) p; writeln(a);
配列
動的配列を別の動的配列にキャストすることは、 配列の長さと要素サイズを掛けた値が一致する場合のみ可能である。キャストは 型変換として行われ、配列の長さは要素サイズの変更に合わせて調整される。 一致しない場合は、実行時エラーが発生する。
byte[] a = [1,2,3]; //auto b = cast(int[])a; // 実行時エラー: 配列キャストのずれ int[] c = [1, 2, 3]; auto d = cast(byte[])c; // OK // 以下のように表示される: // [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] writeln(d);
静的配列
静的配列を別の静的配列にキャストすることは、 配列の長さと要素サイズの積が一致する場合のみ可能であり、不一致の場合は 違法となる。 キャストは型ペイント(再解釈キャストとも呼ばれる)として実行される。 配列の内容は変更されない。
byte[16] b = 3; // 各要素を3に設定する assert(b[0] == 0x03); int[4] ia = cast(int[4]) b; // 要素を16進数で表示する foreach (i; ia) writefln("%x", i); /* prints: 3030303 3030303 3030303 3030303 */
整数
整数をより小さい整数にキャストすると、 値は最下位ビットの方向に切り捨てられる。 ターゲットの型が符号付きで、切り捨て後に最上位ビットがセットされる場合、 そのビットは値から失われ、 符号ビットがセットされる。
uint a = 260; auto b = cast(ubyte) a; assert(b == 4); // 260 & 0xffのように切り捨てられる int c = 128; assert(cast(byte)c == -128); // 再解釈される
符号付き型と符号なし型の間で変換を行う場合、 変換先の型が変換元の値を表現できない場合は、値が再解釈される。
short c = -1; ushort d = c; assert(d == ushort.max); assert(uint(c) == uint.max); ubyte e = 255; byte f = e; assert(f == -1); // 再解釈される assert(short(e) == 255); // 変更なし
浮動小数点数
浮動小数点リテラルをある型から別の型にキャストすると 型が変更されるが、内部的には定数圧縮の目的で完全な精度が保持される 。
void test() { real a = 3.40483L; real b; b = 3.40483; // リテラルを倍精度に切り捨てない assert(a == b); assert(a == 3.40483); assert(a == 3.40483L); assert(a == 3.40483F); double d = 3.40483; // 変数に代入されたときにリテラルを切り捨てる assert(d != a); // もはや同じではない const double x = 3.40483; // constへの代入は切り捨てられない assert(x == a); // 初期化子が見えている場合は切り捨てられる }
浮動小数点値を整数型にキャストすることは、 切り捨てによる整数への変換と同等である。浮動小数点 値が整数型の範囲外である場合、キャストは 無効な結果を生成する(これはC、C++でも同様である)。
void main() { int a = cast(int) 0.8f; assert(a == 0); long b = cast(long) 1.5; assert(b == 1L); long c = cast(long) -1.5; assert(c == -1); // floatがオーバーフローした場合、キャストは以下の整数値を返却する。 // 80000000_00000000H(64ビットオペランド)または80000000H(32ビットオペランド)。 long d = cast(long) float.max; assert(d == long.min); int e = cast(int) (1234.5 + int.max); assert(e == int.min); // 16ビットまたは8ビットで表現される型の場合、 // 結果は32ビット型と同じだが、最上位ビットは無視される。 short f = cast(short) float.max; assert(f == 0); }
構造体
値 v を構造体 S にキャストする場合、値が 同じ型の構造体でない場合、これは以下と同等である。
S(v)
修飾子キャスト
CastQual: cast ( TypeCtorsopt ) UnaryExpression
A CastQual は UnaryExpression の型における修飾子を置き換える。
shared int x; static assert(is(typeof(cast(const)x) == const int));
型または修飾子を指定せずにキャストすると、const 、immutable 、shared 、inoutのいずれかの最上位レベルの型修飾子が UnaryExpressionの型から削除される。
shared int x; static assert(is(typeof(cast()x) == int));
キャストするvoid
式をvoid 型にキャストすることは、 結果が使用されていないことを示すために許可されている。ExpressionStatementでは、 「効果がない」というエラーを回避するために適切に使用できる。
void foo(lazy void exp) {} void main() { foo(10); // NG - 式'10'は何の効果もない foo(cast(void)10); // OK }
スロー式
ThrowExpression: throw AssignExpression
AssignExpressionは評価され、ThrowableまたはThrowable から派生したクラスへの参照を生成しなければならない。 この参照は例外としてスローされ、 現在の制御フローを中断して、try-statementの適切なcatch 節で処理が継続される。このプロセスでは、 該当するtry ブロックに入ってから渡されたすべてのscope (exit) /scope (failure)が 実行される。
throw new Exception("message");
Throwable は、immutable 、const 、inout 、またはshared として修飾されてはならない。 実行時では、スローされたオブジェクトを変更することがあり(例えば、スタックトレースを含めるなど)、 これはconst またはimmutable オブジェクトに違反する可能性がある。
ThrowExpression は別の式の中にネストすることができる。
void foo(int function() f) {} void main() { foo(() => throw new Exception()); }
ThrowExpressionの型は noreturn。
べき乗式
PowExpression: PostfixExpression PostfixExpression ^^ UnaryExpression
PowExpressionは、左オペランドを右オペランドの累乗まで上げる。
Postfix Expressions
PostfixExpression: PrimaryExpression PostfixExpression . Identifier PostfixExpression . TemplateInstance PostfixExpression . NewExpression PostfixExpression ++ PostfixExpression -- PostfixExpression ( NamedArgumentListopt ) TypeCtorsopt BasicType ( NamedArgumentListopt ) PostfixExpression IndexOperation PostfixExpression SliceOperation
Operation | Description |
---|---|
. 識別子 | いずれか: |
. NewExpression | ネストされたクラスのインスタンス化 |
++ | 使用後にインクリメント -評価順を参照 |
-- | 使用後にデクリメントする |
(args) | いずれか:
|
IndexOperation | 単一要素の選択 |
スライス操作 | 一連の要素を選択する |
ポストフィックス引数リスト
ArgumentList: AssignExpression AssignExpression , AssignExpression , ArgumentList NamedArgumentList: NamedArgument NamedArgument , NamedArgument , NamedArgumentList NamedArgument: Identifier : AssignExpression AssignExpression
呼び出し可能な式は、括弧で囲まれた名前付き引数のリストの前に置くことができる。 次の式を呼び出すことができる。
- 関数
- 関数ポインタ
- デリゲート
- 集約型インスタンスで定義された opCall
void f(int, int); void g() { f(5, 6); (&f)(5, 6); }
引数とパラメータの一致
NamedArgumentList 内の引数は、関数パラメータに次のように対応付けられる。
- 最初の引数に名前がない場合は、最初の関数パラメータに割り当てられる。
- 名前付き引数は、同じ名前の関数パラメータに割り当てられる。 そのようなパラメータが存在しない場合はエラーとなる。
- 名前のない引数は、前の引数のパラメータに関連する次のパラメータに割り当てられる。 そのようなパラメータが存在しない場合はエラーとなる。すなわち、前の引数が最後のパラメータに割り当てられている場合などである。
- パラメータを複数回割り当てることはエラーである。
- パラメータに値を代入しないこともエラーであるが、 ただし、パラメータにデフォルト引数がある場合はこの限りではない。
引数リストで型を構築する
型は引数のリストの前に置くことができる。参照:
インデックス操作
IndexOperation: [ ArgumentList ]
基本の PostfixExpressionが評価される。 特別な変数$ が宣言され、 基本の PostfixExpression の要素数(利用可能な場合)に設定される。 ArgumentList の評価用に新しい宣言スコープが作成され、 $ がそのスコープのみに表示される。
PostfixExpression が静的配列型または動的配列型の式である場合、 添字指定の結果は、配列の i 番目の要素の"l値" となる。ここで、i は ArgumentList から評価された整数である。 PostfixExpressionがポインタである場合、p 、結果は *(p + i) となる(「ポインタ演算」を参照)。
ベースの PostfixExpressionがValueSeq の場合 、ArgumentListは1つの引数のみで構成されなければならず、 静的に整数の定数として評価可能でなければならない。 その整数の定数 n は、 ValueSeq の n 番目の式を選択し、これは IndexOperation の結果である。 n が ValueSeq の範囲外の場合はエラーとなる。
インデックス演算子はオーバーロードすることができる。 ArgumentListで複数のインデックスを使用することは、演算子オーバーロードの場合のみサポートされる。
スライス操作
SliceOperation: [ ] [ Slice ,opt ] Slice: AssignExpression AssignExpression , Slice AssignExpression .. AssignExpression AssignExpression .. AssignExpression , Slice
PostfixExpressionの基本部分が評価される。 特別な変数$ が宣言され、 PostfixExpressionの要素数(利用可能な場合)に設定される。 AssignExpressionの評価用に、新しい宣言スコープが作成される 。..AssignExpressionと$ は そのスコープのみに表示される。
PostfixExpression のベースが静的または動的配列の場合 a 、スライスの結果は、 a[i] からa[j-1] までの要素を参照する動的配列となる。ここで、i およびj は、それぞれ 最初の AssignExpressionおよび 2 番目の AssignExpressionから評価された整数である。
PostfixExpressionのベースがポインタの場合、p 、 p[i] からp[j-1]までの要素を参照する動的配列が結果として返される。ここで、i とj は、 それぞれ最初のAssignExpressionと2番目のAssignExpressionから評価された整数である。
PostfixExpressionのベース がValueSeqの場合、 スライスの結果は、 上限と下限から形成される新しいValueSeqとなり、 静的に整数の定数として評価されなければならない。 これらの範囲が範囲外の場合はエラーとなる。
最初のAssignExpressionは、スライスの下限値(含む)と見なされ 、2番目の AssignExpression は 上限値(含まない)と見なされる。 式の結果は、PostfixExpression の要素のスライスとなる。
[ ] 形式が使用された場合、スライスはベースPostfixExpression内のすべての要素となる。 ベース式はポインタにすることはできない。
スライス演算子はオーバーロードすることができる。 複数のスライスを使用することは、演算子オーバーロードの場合のみサポートされる 。
SliceOperationは変更不可の"l値"ではない。
スライス変換を静的配列に
スライスの境界がコンパイル時に判明する場合は、スライス式は 静的配列の左辺値に暗黙的に変換できる可能性がある。例えば:
arr[a .. b] // typed T[]
a とb の両方が整数(定数展開される可能性がある)である場合、 スライス式はT[b - a] 型の静的配列に変換できる。
void f(int[2] sa) {} int[] arr = [1, 2, 3]; void test() { //f(arr); // エラー、変換できない f(arr[1 .. 3]); // OK //f(arr[0 .. 3]); // エラー int[2] g() { return arr[0 .. 2]; } }
void bar(ref int[2] a) { assert(a == [2, 3]); a = [4, 5]; } void main() { int[] arr = [1, 2, 3]; // lvalueをスライスするとlvalueが得られる bar(arr[1 .. 3]); assert(arr == [1, 4, 5]); }
主要な式
PrimaryExpression: Identifier . Identifier TemplateInstance . TemplateInstance $ LiteralExpression AssertExpression MixinExpression ImportExpression NewExpression FundamentalType . Identifier TypeCtoropt ( Type ) . Identifier ( Type ) . TemplateInstance FundamentalType ( NamedArgumentListopt ) TypeCtoropt ( Type ) ( NamedArgumentListopt ) Typeof TypeidExpression IsExpression ( Expression ) SpecialKeyword TraitsExpression LiteralExpression: this super null true false IntegerLiteral FloatLiteral CharacterLiteral StringLiteral InterpolationExpressionSequence ArrayLiteral AssocArrayLiteral FunctionLiteral
Expression | Description |
---|---|
. 識別子 | モジュールスコープ演算子 |
$ | インデックス化/スライスされるオブジェクトの要素数。 |
( 型 ). 識別子 | 型プロパティまたは 型の静的メンバにアクセスする。 |
FundamentalType (arg) | オプション引数を持つスカラー型の 均一な構造。 |
( 型 )(args) | オプション引数を持つ型を構築する。 |
( 式 ) | 式を評価する -部分式として便利である。 |
これ
コンストラクタまたは非staticメンバ関数内では、this は 関数が呼び出されたオブジェクトへの参照として解決される。
typeof(this)は、 複合型定義内のどこでも有効である。 クラスメンバ関数が明示的な参照とともに呼び出される場合 typeof(this) 、非仮想関数が呼び出される。
class A { char get() { return 'A'; } char foo() { return typeof(this).get(); } // `A.get`を呼び出す char bar() { return this.get(); } // 動的、単なる`get()`と同じ } class B : A { override char get() { return 'B'; } } void main() { B b = new B(); assert(b.foo() == 'A'); assert(b.bar() == 'B'); }
this への割り当てはクラスには許可されていない。
参照:
super
super this と同一であるが、 の基底クラスにキャストされる点が異なる。 基底クラスが存在しない場合はエラーとなる。 ( クラスで基底クラスを持たないのは だけであるが、 クラスは指定しない限り基底クラスを持たないことに注意。) メンバ関数が明示的に への参照で呼び出された場合、 this extern(D) Object extern(C++) super
super への代入は許可されていない。
参照:基本クラスの構築。
null
null ポインタ、関数へのポインタ、デリゲート、 ポインタ、関数へのポインタ、デリゲート、 ダイナミック配列、連想配列、 クラスオブジェクトの null値を表す。型にキャストされていない場合、 単一型である が割り当てられ、 ポインタ、関数へのポインタ、デリゲートなどのnull値への正確な変換となる。 型にキャストされた後は、このような変換は暗黙的になるが、 正確ではなくなる。 typeof(null)
文字列リテラル
StringLiteral文法を参照。
文字列リテラルは読み取り専用である。 文字列後置詞を伴わない文字列リテラルは、 暗黙的に以下のいずれかの型に変換できる。 これらは同等に扱われる。 xml-ph-0000@deepl.internal
immutable(char)* |
immutable(wchar)* |
immutable(dchar)* |
immutable(char)[] |
immutable(wchar)[] |
immutable(dchar)[] |
デフォルトでは、文字列リテラルは動的配列として型付けされるが、要素 数はコンパイル時に判明している。そのため、すべての文字列リテラルは 暗黙のうちに不変の静的配列に変換される。
void foo(char[2] a) { assert(a[0] == 'b'); } void bar(ref const char[2] a) { assert(a == "bc"); } void main() { foo("bc"); foo("b"); // OK //foo("bcd"); // エラー、文字数が多すぎる bar("bc"); // OK、同じ長さ //bar("b"); // エラー、長さが一致しなければならない }
文字列リテラルは、同じ長さまたはそれより長い静的配列の右辺値に変換される。 余分な要素はゼロで埋められる。文字列リテラルは 同じ長さの静的配列の左辺値にも変換できる。
文字列リテラルには'\0' が付加されており、 これにより、ヌル文字で終端するconst char* 文字列を期待する C または C++ 関数に簡単に渡すことができる。 '\0' は文字列リテラルの.length プロパティには含まれない。
文字列リテラルの連結には、 ~ 演算子を使用する必要があり、コンパイル時に解決される。 演算子を介さない"C言語のスタイル"の暗黙的な連結は エラーが発生しやすく、Dではサポートされていない。
styleスタイル16進文字列リテラル
16進文字列リテラルはテキストデータに限らないバイナリデータを含むため、他の文字列リテラルよりもさらに多くの変換が可能である。
16進文字列リテラルは、暗黙的に定数byte[] またはubyte[] に変換される。
xml-ph-0000@deepl.internalimmutable ubyte[] b = x"3F 80 00 00"; const byte[] c = x"3F 80 00 00";
1より大きなサイズの整数配列に明示的にキャストできる16進文字列リテラル。 16進文字列ではビッグエンディアンバイト順が想定される。
static immutable uint[] data = cast(immutable uint[]) x"AABBCCDD"; static assert(data[0] == 0xAABBCCDD);
これは、16進文字列の長さが配列要素のバイトサイズの倍数であることを必要とする。
xml-ph-0000@deepl.internalstatic e = cast(immutable ushort[]) x"AA BB CC"; // エラー、3バイトの長さは2の倍数ではない、`ushort`のサイズ
16進文字列リテラルが定数展開されると、結果はもはや16進文字列リテラルとはみなされない
static immutable byte[] b = x"AA" ~ "G"; // エラー: `string`を`immutable byte[]`に変換できない
配列リテラル
ArrayLiteral: [ ArrayMemberInitializationsopt ] ArrayMemberInitializations: ArrayMemberInitialization ArrayMemberInitialization , ArrayMemberInitialization , ArrayMemberInitializations ArrayMemberInitialization: NonVoidInitializer AssignExpression : NonVoidInitializer
配列リテラルとは、角カッコ[ と] の間にカンマで区切って並べた式のリストである。 これらの式は動的配列の要素となる。 配列の長さは要素の数である。
配列の要素型は、すべての要素の共通型として推論され、 各式は暗黙的にその型に変換される。 配列の型が指定されている場合、リテラルの要素は 暗黙的に指定された要素型に変換される。
auto a1 = [1, 2, 3]; // 型はint[]で、要素は1、2、3である auto a2 = [1u, 2, 3]; // 型はuint[]で、要素は1u、2u、3uである byte[] a3 = [1, 2, 3]; // OK byte[] a4 = [128]; // エラー
int[2] sa = [1, 2]; // OK int[2] sb = [1]; // エラー
ArrayMemberInitializationがValueSeq の場合、 ValueSeq の要素は シーケンスの代わりに式として挿入される。
配列リテラルはメモリ管理ヒープに割り当てられる。 したがって、関数から安全に返すことができる。
int[] foo() { return [1, 2, 3]; }
特定のインデックスの要素を初期化するには、 AssignExpression : NonVoidInitializer構文を使用する。 AssignExpressionはコンパイル時に指定する必要がある。 欠落している要素はすべて、要素型のデフォルト値に初期化される。 配列型が指定されていない場合、リテラルは 連想配列として解析される ことに注意すること。
int n = 4; auto aa = [0:1, 3:n]; // 連想配列`int[int]` int[] a = [1, 3:n, 5]; assert(a == [1, 0, 0, n, 5]); //int[] e = [n:2]; // エラー、コンパイル時にnがわからない
キャスト
配列リテラルが別の配列型にキャストされる場合、 配列の各要素は新しい要素型にキャストされる。 リテラルではない配列がキャストされる場合、配列は 新しい型として再解釈され、長さが再計算される。
// 配列リテラルをキャストする const ubyte[] ct = cast(ubyte[]) [257, 257]; // これは等価である: // const ubyte[] ct = [cast(ubyte) 257, cast(ubyte) 257]; writeln(ct); // [1, 1]を書き込む // 他の配列式をキャストする // --> CastExpressionの通常の動作 byte[] arr = [1, 1]; short[] rt = cast(short[]) arr; writeln(rt); // [257]を書き込む
連想配列リテラル
AssocArrayLiteral: [ KeyValuePairs ] KeyValuePairs: KeyValuePair KeyValuePair , KeyValuePairs KeyValuePair: KeyExpression : ValueExpression KeyExpression: AssignExpression ValueExpression: AssignExpression
連想配列リテラルは、 キーと:値のペアを 角カッコ[ と] で囲み、カンマで区切った リストである。このリストは空であってはならない。 すべてのキーの共通型は、連想配列のキー型と見なされ、 すべてのキーは暗黙的にその型に変換される。 すべての値の共通型は、連想配列の値型と見なされ、 すべての値は暗黙的にその型に変換される 。 連想配列リテラルは、静的に初期化する目的では使用できない 。
[21u: "he", 38: "ho", 2: "hi"]; // 型はstring[uint] // キーは21u, 38u, 2u // 値は "he", "ho", "hi"
KeyValuePairsのキーまたは値のいずれかが ValueSeq の場合、その ValueSeq の要素は 配列の代わりに引数として挿入される。
連想配列の初期化子には重複するキーを含めることができるが、 その場合は最後に辞書順で遭遇したKeyValuePair が 格納される。
auto aa = [21: "he", 38: "ho", 2: "hi", 2:"bye"]; assert(aa[2] == "bye")
関数リテラル
FunctionLiteral: function RefOrAutoRefopt Typeopt ParameterWithAttributesopt FunctionLiteralBody2 delegate RefOrAutoRefopt Typeopt ParameterWithMemberAttributesopt FunctionLiteralBody2 RefOrAutoRefopt ParameterWithMemberAttributes FunctionLiteralBody2 BlockStatement Identifier => AssignExpression ParameterWithAttributes: Parameters FunctionAttributesopt ParameterWithMemberAttributes: Parameters MemberFunctionAttributesopt FunctionLiteralBody2: => AssignExpression SpecifiedFunctionBody RefOrAutoRef: ref auto ref
関数リテラルは、匿名関数 および匿名デリゲートを式に直接埋め込むことを可能にする。 短い関数リテラルはラムダとして知られている。
- 型は関数またはデリゲートの戻り値の型である。 省略した場合は推論される。
- ParameterWithAttributesまたはParameterWithMemberAttributesを使用して、関数のパラメータを指定できる。 省略した場合は、関数のデフォルトは空のパラメータリストとなる( ) 。
- パラメータの型は省略可能である。
- 関数リテラルの型は、 デリゲートまたは関数へのポインタである。
例えば:
int function(char c) fp; // 関数へのポインタを宣言する void test() { static int foo(char c) { return 6; } fp = &foo; }は、次のものとまったく同等である。
int function(char c) fp; void test() { fp = function int(char c) { return 6; }; }
FunctionLiteralBody2が囲み関数の非静的ローカル変数にアクセスする場合は、デリゲートが必要である。
は、次のコードとまったく同じである。int abc(int delegate(int i)); void test() { int b = 3; int foo(int c) { return 6 + b; } abc(&foo); }は、次のコードとまったく同じである。
int abc(int delegate(int i)); void test() { int b = 3; abc( delegate int(int c) { return 6 + b; } ); }
ref の使用は、戻り値が参照によって返されることを宣言する。
void main() { int x; auto dg = delegate ref int() { return x; }; dg() = 3; assert(x == 3); }
デリゲート推論
リテラルがfunction またはdelegate を省略しており、 文脈から想定される型がない場合、 それが囲む関数内の変数にアクセスしている場合はデリゲートと推測され、 そうでない場合は関数ポインタとなる。
void test() { int b = 3; auto fp = (uint c) { return c * 2; }; // 関数ポインタとして推論される auto dg = (int c) { return 6 + b; }; // デリゲートとして推論される static assert(!is(typeof(fp) == delegate)); static assert(is(typeof(dg) == delegate)); }
デリゲートが想定されている場合、リテラルは、 たとえそれが囲む関数の変数にアクセスしていなくても、デリゲートとして推論される。
void abc(int delegate(int i)) {} void def(uint function(uint s)) {} void test() { int b = 3; abc( (int c) { return 6 + b; } ); // デリゲートとして推論される abc( (int c) { return c * 2; } ); // デリゲートとして推論される def( (uint c) { return c * 2; } ); // 関数として推論される //def( (uint c) { return c * b; } ); // エラー! // FunctionLiteralはbにアクセスしているため、その型はデリゲートと推論される。 // しかし、defはデリゲート引数を受け取ることができない。 }
パラメータの型推論
関数リテラルの型がそのコンテキストから一意に決定できる場合、 パラメータの型推論が可能である。
void foo(int function(int) fp); void test() { int function(int) fp = (n) { return n * 2; }; // パラメータnの型はintと推測される。 foo((n) { return n * 2; }); // パラメータnの型はintと推測される。 }
auto fp = (i) { return 1; }; // エラー、`i`の型を推論できない
関数リテラルエイリアス
関数リテラルはエイリアス化できる。 未指定のパラメータ型を持つ関数リテラルをエイリアス化すると、 関数テンプレートが 生成され、リテラルの未指定のパラメータ型ごとに型パラメータが指定される。 リテラルの型推論は、テンプレートがインスタンス化されたときに実行される。
alias fpt = (i) { return i; }; // OK、`i`の型を推論する //auto fpt(T)(T i) { return i; } // 等価である auto v = fpt(4); // `i`はintと推論される auto d = fpt(10.3); // `i`はdoubleと推論される alias fp = fpt!float; auto f = fp(0); // fはfloatである
戻り値の型推論
FunctionLiteralの戻り値の型は、 AssignExpression、または BlockStatement内の ReturnStatementsのいずれかから 推論できる。コンテキストから期待される型が異なり、 最初に推論された戻り値の型が暗黙的に期待される型に変換される場合、 戻り値の型は期待される型として推論される。
auto fi = (int i) { return i; }; static assert(is(typeof(fi(5)) == int)); long function(int) fl = (int i) { return i; }; static assert(is(typeof(fl(5)) == long));
短縮構文(nullary)
BlockStatement関数本体がある場合、関数リテラルではパラメータを完全に省略できる。
auto f = { writeln("hi"); }; // OK、fは`void function()`型である f(); { writeln("hi"); }(); // エラー () { writeln("hi"); }(); // OK
void loop(int n, void delegate() statement) { foreach (_; 0 .. n) { statement(); } } void main() { int n = 0; loop(5, { n += 1; }); assert(n == 5); }
短縮されたボディの構文
=> AssignExpression の構文は、{ return AssignExpression; } と同等である。
void main() { auto i = 3; auto twice = function (int x) => x * 2; assert(twice(i) == 6); auto square = delegate () => i * i; assert(square() == 9); auto n = 5; auto mul_n = (int x) => x * n; assert(mul_n(i) == 15); }
構文Identifier => AssignExpression は(Identifier) { return AssignExpression; } と同等である。
// 次の2つの宣言は等価である alias fp = i => 1; alias fp = (i) { return 1; };
int motor(alias fp)(int i) { return fp(i) + 1; } int engine() { return motor!(i => i * 2)(6); // 返却値 13 }xml-ph-0000@deepl.internal
組み込みのスカラー型に対する統一された構文
組み込みのスカラー型の暗黙の変換は、 関数呼び出し構文を使用することで明示的に表現できる。例えば:
auto a = short(1); // 暗黙のうちに整数リテラル'1'をshortに変換する auto b = double(a); // 暗黙的にshort変数'a'をdoubleに変換する auto c = byte(128); // エラー、128はバイトでは表現できない
引数が省略された場合、スカラー型のデフォルトの構築を意味する。
auto a = ushort(); // ushort.initと同じ auto b = wchar(); // wchar.initと同じ
引数に名前を付けることはできない。
auto a = short(x: 1); // エラー
参照:通常の算術変換。
arithmetic conversion算術変換式を主張する
AssertExpression: assert ( AssertArguments ) AssertArguments: AssignExpression ,opt AssignExpression , AssignExpression ,opt
最初のAssignExpressionが評価され、ブール値に変換される。 値がtrue でない場合、アサートエラーが発生し、 プログラムは無効な状態に入る。
int i = fun(); assert(i > 0);
AssertExpressionは、それが unittestまたはin 契約
最初の AssignExpressionが、 クラス不変が存在するクラスのインスタンスへの参照である場合、 クラス不変は保持されなければならない。
最初の AssignExpressionが構造体インスタンスへのポインタであり、 その構造体インスタンスに対して 構造体インバリアントが存在する場合、構造体インバリアントを保持しなければならない。
AssertExpressionの型はvoid である。
- 特別なCPU命令の実行により即座に停止する
- プログラムを中断する
- 対応する C ランタイムライブラリ内の assert 失敗関数を呼び出す
- DランタイムライブラリでAssertError 例外をスローする
auto x = 4; assert(x < 3);使用時には、上記のコードはAssertError を4 >= 3 というメッセージとともにスローする。
- AssignExpression のいずれにおいても、その後のコードに依存する副作用は発生しない 。
- アサート式はプログラムのバグを検出することを目的としている。 入力エラーや環境エラーの検出には使用しないこと。
- アサートエラー発生後に通常の実行を再開しようとしてはならない。
コンパイル時の評価
最初の AssignExpression がすべてコンパイル時の定数で構成され、false と評価される場合は、特別なケースであり、 その後の文は到達不能コードであることを意味する。 コンパイル時関数実行(CTFE)は試行されない。
最初の AssignExpressionがコンパイル時にfalseと評価される場合、実装では異なる処理が行われる可能性がある。 他のassertが無視される場合でも、HLT 命令または同等の命令が生成される可能性がある。
関連項目: static assert。
メッセージをアサートする
2番目のAssignExpression が存在する場合、暗黙的にconst(char)[] 型に変換できる必要がある。 これが存在する場合、実装ではこれを評価し、 アサートが失敗した際に結果のメッセージを表示できる。
xml-ph-0000@deepl.internalvoid main() { assert(0, "an" ~ " error message"); }
コンパイルして実行すると、以下のメッセージが表示される。
core.exception.AssertError@test.d(3) an error message0email xml-ph-0000@deepl.internalxml-ph-0000@deepl.internal
ミックスイン式
MixinExpression: mixin ( ArgumentList )
ArgumentList内の 各AssignExpressionは コンパイル時に評価され、結果は文字列として表現できるものでなければならない。 結果の文字列は結合されて1つの文字列が形成される。 文字列のテキスト内容は、有効な式としてコンパイル可能なものでなければならず、 そのようにコンパイルされる。
int foo(int x) { return mixin("x +", 1) * 7; // ((x + 1) * 7)と同じ }
式をインポートする
ImportExpression: import ( AssignExpression )
AssignExpression は、コンパイル時に 定数文字列として評価されなければならない。 文字列のテキスト内容はファイル名として解釈される 。ファイルが読み込まれ、ファイルの正確な内容が 文字列リテラルとなる。
実装によっては、ディレクトリ・トラバーサルによるセキュリティ脆弱性を回避するために、ファイル名を制限する場合がある。 考えられる制限としては、ファイル名内のパス構成要素を一切許可しないことが挙げられる。
デフォルトでは、インポート式は、-J スイッチで1つ以上のパスが渡されない限り、コンパイルされないことに注意。これは、 インポートするファイルの場所をコンパイラに指示する。これはセキュリティ機能である。
void foo() { // ファイルfoo.txtの内容を表示する writeln(import("foo.txt")); }
新しい式
NewExpression: new Type new Type [ AssignExpression ] new Type ( NamedArgumentListopt ) NewAnonClassExpression
NewExpressionsは、 ガベージ コレクションされたヒープ上にデフォルトでメモリを割り当てる。
new 型フォームは型のインスタンスを構築し、それをデフォルトで初期化する。
Type(NamedArgumentList)フォームでは、同じ型の単一の初期化子、または より複雑な型の複数の引数を渡すことができる。 クラス型の場合、NamedArgumentListはクラスコンストラクタに渡される。 動的配列の場合、引数は初期配列長を設定する。 多次元動的配列の場合、各引数は 初期長に対応する(下記参照)。
int* i = new int; assert(*i == 0); i = new int(5); assert(*i == 5); Object o = new Object; Exception e = new Exception("info"); auto a = new int[](2); assert(a.length == 2);
Type[AssignExpression]形式は、 AssignExpressionと同じ長さの動的配列を割り当てる。 代わりに動的配列を割り当てる場合は、より一般的なType(NamedArgumentList)形式を使用することが推奨される。
結果は、 他の修飾子に暗黙的に変換できるユニークな式となる。
immutable o = new Object;
クラスのインスタンス化
NewExpression がクラス型と共に、 ストレージクラスを持つ関数ローカル変数の初期化子として使用される場合、 scopeストレージクラスを持つ場合、 そのインスタンスはスタック上に割り当てられる。
new ネストされたクラスの割り当てにも使用できる。
多次元配列
多次元配列を割り当てる場合、宣言は プレフィックス配列の宣言順と同じ順序で読み込まれる。
char[][] foo; // 文字列の動的配列 ... foo = new char[][30]; // 30個の文字列の配列を確保する
上記の割り当ては、次のように書くこともできる。
foo = new char[][](30); // 30個の文字列の配列を確保する
ネストした配列を割り当てるには、複数の引数を使用できる。
int[][][] bar; bar = new int[][][](5, 20, 30); assert(bar.length == 5); assert(bar[0].length == 20); assert(bar[0][0].length == 30);
bar = new int[][][5]; foreach (ref a; bar) { a = new int[][20]; foreach (ref b; a) { b = new int[30]; } }
Typeid 式
TypeidExpression: typeid ( Type ) typeid ( Expression )
"型"の場合、"型"に対応するクラスのインスタンスを返す TypeInfo "型"に対応する
IfExpression、式の型に対応するクラスのインスタンスを返す TypeInfo 式の型に対応する クラスを返す。 型がクラスである場合、動的型(すなわち、最も派生した型)のTypeInfoを返す。 式は常に実行される。
class A { } class B : A { } void main() { import std.stdio; writeln(typeid(int)); // int uint i; writeln(typeid(i++)); // uint writeln(i); // 1 A a = new B(); writeln(typeid(a)); // B writeln(typeid(typeof(a))); // A }
式は
IsExpression: is ( Type ) is ( Type : TypeSpecialization ) is ( Type == TypeSpecialization ) is ( Type : TypeSpecialization , TemplateParameterList ) is ( Type == TypeSpecialization , TemplateParameterList ) is ( Type Identifier ) is ( Type Identifier : TypeSpecialization ) is ( Type Identifier == TypeSpecialization ) is ( Type Identifier : TypeSpecialization , TemplateParameterList ) is ( Type Identifier == TypeSpecialization , TemplateParameterList ) TypeSpecialization: Type TypeCtor struct union class interface enum __vector function delegate super return __parameters module package
IsExpressionはコンパイル時に評価され、 式が有効な型であるかを確認するために使用される。さらに、 以下の形式も使用できる。
- 型の等価性を比較
- ある型が別の型に暗黙的に変換できるかどうかを判断
- パターンマッチングを使用して型のサブタイプを推論する
- 型のテンプレート引数を推論する
IsExpressionの結果は、条件が満たされるとtrue、満たされないとfalse となるブーリアンである。
Typeはテストされる型である。 構文的に正しい必要があるが 、意味的に正しい必要はない。 意味的に正しくない場合は、条件は満たされない。
型特殊化とは、型がパターンマッチされる型である。
IsExpressionsは、 typeof式の型が正しくチェックされているかどうかを確認するために使用できる。 例えば、is(typeof(foo)) は、foo が有効な型である場合にtrue を返す。
基本フォーム
is ( 型 )
型が意味的に正しい場合は条件を満たす。 型は構文的に正しい必要がある。
pragma(msg, is(5)); // エラー pragma(msg, is([][])); // エラー
int i; static assert(is(int)); static assert(is(typeof(i))); // 同一 static assert(!is(Undefined)); static assert(!is(typeof(int))); // intは式ではない static assert(!is(i)); // iは値である alias Func = int(int); // 関数型である static assert(is(Func)); static assert(!is(Func[])); // 関数の配列は許可されないため、失敗する
is ( 型 : 型特殊化 )
型が意味的に正しく、かつ TypeSpecialization と 同じであるか、または暗黙的に変換できる場合は、条件が満たされる。 TypeSpecialization は型のみ許可される。
alias Bar = short; static assert(is(Bar : int)); // shortは暗黙的にintに変換される static assert(!is(Bar : string));
is ( 型 == TypeSpecialization )
TypeSpecializationが型である場合、 Typeが意味的に正しく、かつ TypeSpecialization と同じ型である場合に条件が満たされる。
alias Bar = short; static assert(is(Bar == short)); static assert(!is(Bar == int));
TypeSpecializationがTypeCtorの場合、 Type がその TypeCtor であれば条件を満たす。
static assert(is(const int == const)); static assert(is(const int[] == const)); static assert(!is(const(int)[] == const)); // headは変更可能である static assert(!is(immutable int == const));
TypeSpecialization が以下のいずれかである場合 struct union class interface enum __vector function delegate module package の場合、Type がこれらのいずれかであれば条件を満たす。
static assert(is(Object == class)); static assert(is(ModuleInfo == struct)); static assert(!is(int == class));
module とpackage のフォームは、他のフォームとは異なり、"型"がシンボル(型ではない)である場合に満たされる。 代わりに、isModule とisPackageの __traits を使用すべきである。 パッケージモジュールは、パッケージとモジュールの両方であるとみなされる。
TypeSpecializationもこれらのキーワードの1つである。
keyword | condition |
---|---|
super | true 型がクラスまたはインターフェイスである場合 |
return | true 型が関数、デリゲート、関数ポインタの場合 |
__parameters | true 型が関数、デリゲート、関数ポインタの場合 |
class C {} static assert(is(C == super)); void foo(int i); static assert(!is(foo == return)); static assert(is(typeof(foo) == return)); static assert(is(typeof(foo) == __parameters));
See also: トレイト。
TraitTrait識別子形式
条件が満たされる場合、Identifierは結果の型のエイリアスとして宣言される。 Identifierフォームは、 IsExpression がStaticIfConditionまたはStaticAssertの最初の引数に現れる場合にのみ使用できる。 xml-ph-0000@deepl.internal
is ( 型 識別子 )
型が意味的に正しい場合、条件は満たされる。 その場合、識別子は 型の別名として宣言される。
struct S { int i, j; } static assert(is(typeof(S.i) T) && T.sizeof == 4);
alias Bar = short; void foo() { static if (is(Bar T)) alias S = T; else alias S = long; pragma(msg, S); // short // Tが定義されていれば、スコープに残る if (is(T)) pragma(msg, T); // short //if (is(Bar U)) {} // エラー、ここでUを宣言できない }
is ( 型 Identifier : 型特殊化 )
TypeSpecializationが型である場合、 Typeが意味的に正しく、かつ TypeSpecialization と同じであるか、 または暗黙的に変換できる場合に条件が満たされる。 IdentifierはTypeSpecialization のエイリアスとして宣言される。
alias Bar = int; static if (is(Bar T : int)) alias S = T; else alias S = long; static assert(is(S == int));
TypeSpecializationがIdentifier を含む型パターンである場合、 Identifier の型推論が試みられる。 これは、Typeまたはそれが暗黙的に変換される型に基づいて行われる。 この条件は、型パターンが一致した場合のみ満たされる。
struct S { long* i; alias i this; // Sはlong*に変換される } static if (is(S U : U*)) // SがパターンU*とマッチする { U u; } static assert(is(U == long));
識別子の型が決定される方法は、 テンプレートパラメータの型が TemplateTypeParameterSpecializationによって決定される方法と類似している。
is ( 識別子型 == 型特殊化 )
TypeSpecializationが型である場合、 Typeが意味的に正しく、かつ TypeSpecialization と同じ型である場合に条件が満たされる。 Identifierは、TypeSpecialization のエイリアスとして宣言される。
xml-ph-0000@deepl.internalconst x = 5; static if (is(typeof(x) T == const int)) // satisfied、Tが定義された alias S = T; static assert(is(T)); // Tはスコープ内にある pragma(msg, T); // const int
TypeSpecialization がIdentifierを含む型パターンである場合、 Identifierの型推論がTypeに基づいて試みられる。 型パターンが一致した場合のみ、この条件が満たされる。
alias Foo = long*; static if (is(Foo U == U*)) // FooはパターンU*にマッチする { U u; } static assert(is(U == long));
TypeSpecializationがis(Type == Keyword) 形式の有効なキーワードである場合 、 同様に条件が満たされる。 識別子は以下のように設定される。
keyword | alias type for Identifier |
---|---|
struct | 型 |
union | 型 |
class | 型 |
interface | 型 |
super | ベースクラスとインターフェースのシーケンス |
enum | 列挙型の基本型 |
__vector | ベクトルの静的配列型 |
function | TypeSeqの関数パラメータ型。 C言語およびD言語のスタイルの可変長引数関数の場合は、 可変長引数でないパラメータのみが含まれる。 型安全な可変長引数関数の場合は、... は無視される。 |
delegate | デリゲートの関数型 |
return | 関数、デリゲート、または関数ポインタの戻り値の型 |
__parameters | 関数、デリゲート、または関数ポインタのパラメータシーケンス。 これには、パラメータの型、名前、およびデフォルト値が含まれる。 |
const | 型 |
immutable | 型 |
inout | 型 |
shared | 型 |
module | モジュール |
package | パッケージ |
enum E : byte { Emember } static if (is(E V == enum)) // satisfied、Eは列挙型である V v; // vはバイトであると宣言される static assert(is(V == byte));
パラメータリストフォーム
is ( Type : TypeSpecialization , TemplateParameterList ) is ( Type == TypeSpecialization , TemplateParameterList ) is ( Type Identifier : TypeSpecialization , TemplateParameterList ) is ( Type Identifier == TypeSpecialization , TemplateParameterList )
より複雑な型はパターンマッチングできる。 TemplateParameterListは、 パターンマッチングされた部分に基づいてシンボルを宣言する。 暗示的なテンプレートパラメータのマッチング方法と類似している。
Example: テンプレートのインスタンス化に一致する
struct Tuple(T...) { // ... } alias Tup2 = Tuple!(int, string); static if (is(Tup2 : Template!Args, alias Template, Args...)) { static assert(__traits(isSame, Template, Tuple)); static assert(is(Template!(int, string) == Tup2)); // 同じ構造体である } static assert(is(Args[0] == int)); static assert(is(Args[1] == string));
TypeSpecialization がエイリアステンプレートインスタンスの場合、型を一致させることはできない。
struct S(T) {} alias A(T) = S!T; static assert(is(A!int : S!T, T)); //static assert(!is(A!int : A!T, T));
Example: 連想配列の一致
alias AA = long[string]; static if (is(AA T : T[U], U : string)) // T[U]はパターンである { pragma(msg, T); // long pragma(msg, U); // string } // 一致しない、Bはintではない static assert(!is(AA A : A[B], B : int));
Example: 静的配列に一致する
static if (is(int[10] W : W[len], int len)) // W[len]はパターンである { static assert(len == 10); } static assert(is(W == int)); // マッチしない、lenは10でなければならない static assert(!is(int[10] X : X[len], int len : 5));
特殊キーワード
SpecialKeyword: __FILE__ __FILE_FULL_PATH__ __MODULE__ __LINE__ __FUNCTION__ __PRETTY_FUNCTION__xml-ph-0000@deepl.internalは、インスタンス化された時点でのソースファイル名と行番号に展開される。 ソースファイルのパスは コンパイラに任される。
__FILE__ および は、インスタンス化された時点でのソースファイル名と行番号に展開される。 ソースファイルのパスは コンパイラに任される。__LINE__
__FILE_FULL_PATH__ 展開すると、インスタンス化された時点でのソースファイルの絶対パス名となる。
__MODULE__ インスタンス化された時点でのモジュール名に展開される。
は、インスタンス化の時点における関数の完全修飾名に展開される。__FUNCTION__ インスタンス化の時点で、関数の完全修飾名に展開される。
__PRETTY_FUNCTION__ __FUNCTION__ と同様ですが、 関数の戻り値の型、パラメータの型、 および属性も展開します。
例:
module test; import std.stdio; void test(string file = __FILE__, size_t line = __LINE__, string mod = __MODULE__, string func = __FUNCTION__, string pretty = __PRETTY_FUNCTION__, string fileFullPath = __FILE_FULL_PATH__) { writefln("file: '%s', line: '%s', module: '%s',\nfunction: '%s', " ~ "pretty function: '%s',\nfile full path: '%s'", file, line, mod, func, pretty, fileFullPath); } int main(string[] args) { test(); return 0; }
ファイルが /example/test.d にあったと仮定すると、次のような出力が得られる。
file: 'test.d', line: '13', module: 'test', function: 'test.main', pretty function: 'int test.main(string[] args)', file full path: '/example/test.d'
結合性と交換性
実装では、 算術結合性および交換性の規則に従って、 実行スレッド内で目に見える違いが 生じない限り、 "式"の評価を並べ替えることができる。
この規則により、浮動小数点式の結合または交換による並べ替えは一切排除される。
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.109.1
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku