Improve this page Github へのログインが必要です。 簡単な修正は、ここから fork、オンライン編集、pull request ができます。 大きな修正については、 通常の clone で行って下さい。 Page wiki 関連するWikiページを参照・編集 English このページの英語版(原文)

CやC++プログラマの方は、Dの式を非常に親しみやすく感じるでしょう。 いくつか面白い追加もあります。

式は、結果の値を計算するために使用されます。 この値は、変数へ代入したり、判定に用いたり、あるいは無視して捨てることもできます。 式には副作用が伴うことがあります。

評価順序n

以下の二項演算子式は、かならず 左から右に評価されます:

OrExpression, XorExpression, AndExpression, CmpExpression, ShiftExpression, AddExpression, CatExpression, MulExpression, PowExpression, CommaExpression, OrOrExpression, AndAndExpression

以下の二項演算子式は、 実装定義の順序で実行されます:

AssignExpression, 関数引数

未定義の評価順序に依存するようなコードは誤りです。 例えば次のコードは不正です:

i = i++;
c = a + (a = b);
func(++i, ++i);

式の値が評価順序に依存しているとコンパイラが判断したら、 コンパイラはその式をエラーとすることができます。 (必ずエラーになるとは限りませんが。) この種のエラー検知の精度は、実装の質の問題です。

Expression:
    CommaExpression

CommaExpression:
    AssignExpression
    AssignExpression , CommaExpression
まず最初に , の左の式が評価され、 次に右が評価されます。 この式全体の型、及び値は、右の式の型と値となります。

代入式

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
右辺の結果は暗黙に左辺式の型へ変換され、 代入されます。 この式全体の結果型は左辺の式の型で、 値は代入後の左辺値です。

左辺は、lvalue でなくてはなりません。

代入演算子式

代入演算子式、例えば:
a op= b
は、意味的には
a = cast(typeof(a))(a op b)

と同等です、違いは、

条件式

ConditionalExpression:
    OrOrExpression
    OrOrExpression ? Expression : ConditionalExpression
まず、一つ目の式を評価し、 bool値へ変換します。 結果がtrueなら、二番目の式が評価され、 式全体の値はその式の評価結果となります。 結果がfalseなら、三番目の式が評価され、 式全体の値は三番目の式の評価結果です。 もし二番目の式と三番目の式のどちらか一方でもvoid型ならば、 全体の型もvoidになります。そうでなければ、全体の型は 二番目と三番目の式の型から暗黙に変換できる共通の型です。

OrOr 式

OrOrExpression:
    AndAndExpression
    OrOrExpression || AndAndExpression
OrOrExpression の結果は、 右の式の型がvoidならばvoid、 そうでなければboolです。

まず、左の式が評価されます。 評価結果をboolに変換した結果がtrueだったならば、 右の式は評価されません。 全体の型がboolの場合は、 この時は式の値はtrueとなります。 左の式がfalseだった時は、 右の式が評価されます。 全体の型がboolの場合は、 式の値は右の式の評価結果を bool に変化したものです。

AndAnd 式

AndAndExpression:
    OrExpression
    AndAndExpression && OrExpression
    CmpExpression
    AndAndExpression && CmpExpression

AndAndExpression の結果は、右の式の型がvoidならばvoid、 そうでなければboolです。

まず、左の式が評価されます。

評価結果をboolに変換した結果がfalseだったならば、 右の式は評価されません。 全体の型がboolの場合は、 この時は式の値はfalseとなります。

左の式がtrueだった時は、 右の式が評価されます。 全体の型がboolの場合は、 式の値は右の式の評価結果を bool に変換したものです。

ビット演算式

ビット演算式では、オペランドどうしのビット毎の演算が行われます。 オペランドどうしは整数型でなくてはなりません。 まずデフォルトの整数型の昇格が行われ、 その後にビット演算がなされます。

Or 式

OrExpression:
    XorExpression
    OrExpression | XorExpression
ビット毎のOR演算です。

Xor 式

XorExpression:
    AndExpression
    XorExpression ^ AndExpression
ビット毎のXOR演算です。

And 式

AndExpression:
    ShiftExpression
    AndExpression & ShiftExpression
ビット毎のAND演算です。

比較式

CmpExpression:
    ShiftExpression
    EqualExpression
    IdentityExpression
    RelExpression
    InExpression

等値式

EqualExpression:
    ShiftExpression == ShiftExpression
    ShiftExpression != ShiftExpression
等値式では、2つのオペランドが等しいかどうか (==) あるいは等しくないか (!=) がチェックされます。 結果の型はboolで、 比較の前に双方のオペランドは共通の型へと変換されます。

整数かポインタの比較であれば、"等しい" というのは、 "ビットパターンが一致すること" として定義されます。 構造体どうしの"等しい"も、 オブジェクト全体のビットパターンが一致すること、 です。(整列の際の穴の存在も、 コンパイラが初期化時に0で埋めておくなどの方法で 考慮されています)。 浮動小数点数の場合は多少複雑です。-0 と +0 は等しいと判定されます。 どちらか一方でも NaN であれば、 == はfalse、!=はtrueになります。 それ以外の場合は、ビットパターンで比較されます。

複素数の場合、== は以下と同じです:

x.re == y.re && x.im == y.im
!= は次と同じです:
x.re != y.re || x.im != y.im

クラスや構造体オブジェクトであれば、 (a == b) という式は、 a.opEquals(b) と書き換えられ、 (a != b)!a.opEquals(b) と書き換えられます。

演算子 ==!= は、 オブジェクトの内容を比較するものです。従って、 内容を持たない 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で、 比較の前に双方のオペランドは共通の型へと変換されます。

クラスオブジェクトの場合は、二つの参照が同じオブジェクトを 指しているかどうかのチェックになります。 is では、null 参照との比較も可能です。

構造体オブジェクトの場合は、構造体のビット列が同一であること、 として定義されています。

静的あるいは動的な配列の場合は、二つとも同じ配列を指しているかどうか、 で同一性が判定されます。

その他の型に関しては、 同一性は同値性と同じように定義されています。

同一性演算子 is はオーバーロードできません。

関係式

RelExpression:
    ShiftExpression < ShiftExpression
    ShiftExpression <= ShiftExpression
    ShiftExpression > ShiftExpression
    ShiftExpression >= ShiftExpression
    ShiftExpression !<>= ShiftExpression
    ShiftExpression !<> ShiftExpression
    ShiftExpression <> ShiftExpression
    ShiftExpression <>= ShiftExpression
    ShiftExpression !> ShiftExpression
    ShiftExpression !>= ShiftExpression
    ShiftExpression !< ShiftExpression
    ShiftExpression !<= ShiftExpression
まず、オペランド双方に整数の昇格が行われます。 結果の型はboolです。

クラスオブジェクトの場合は、左オペランドに対して Object.opCmp() の呼び出し結果と整数0、を演算子で比較した結果が式全体の値となります。 つまり、関係式 (o1 op o2) の結果は次のように定義されます:

(o1.opCmp(o2) op 0)
どちらかが null の場合、エラーになります。

静的/動的な配列の場合、 "二つの配列の中で等しくない最初の要素" に op を適用した結果が、配列どうしの関係式opの結果となります。 有効な全要素が等しい時は、サイズが小さい配列の方がより"小さい" とします。

整数比較

オペランドがどちらも整数型だった場合、 整数比較が行われます。

整数比較演算子
演算子関係
< 小さい
> 大きい
<= 以下
>= 以上
== 等しい
!= 等しくない

<, <=, >, >= 式の時は、 符号付き整数と符号なし整数の比較はエラーとなります。 明示的にどちらかのオペランドをキャストしてください。

浮動小数点数比較

オペランドがどちらも浮動小数点数型だった場合、 浮動小数点数比較が行われます。

実用に値する浮動小数点数演算を定義するには、 NaN を考慮に入れなくてはいけません。 特に、関係演算のオペランドとしては NaN を取ることができます。 二つの浮動小数点数の関係としては、 より小さい、より大きい、等しい、順序づけ不可能(一方がNaN) の4種類があり、 演算子もこれに応じて14種類の条件を表現しています:

浮動小数点数演算子
演算子 より大きい より小さい 等しい 順序づけ不可能red 例外 関係
== FFTFno 等しい
!= TTFTno 順序づけ不可能, より小さい, より大きい
> TFFFyes より大きい
>= TFTFyes 以上
< FTFFyes より小さい
<= FTTFyes 以下
!<>= FFFTno 順序づけ不可能
<> TTFFyes より小さい, より大きい
<>= TTTFyes より小さい, より大きい, 等しい
!<= TFFTno 順序づけ不可能か、より大きい
!< TFTTno 順序づけ不可能か、以上
!>= FTFTno 順序づけ不可能か、より小さい
!> FTTTno 順序づけ不可能か、以下
!<> FFTTno 順序づけ不可能か、等しい

注:

  1. 浮動小数点数の場合、 (a !op b)!(a op b) と同義ではありません。
  2. "順序づけ不可能" とは、オペランドの一方または両方が NaN という意味です。
  3. "例外" とは、どちらかがNaNだった時に Invalid Exception が発生することを意味します。これは、例外がthrowされるのとは異なります。 Invalid Exception が発生したかどうかのチェックは、 std.c.fenv の関数で行います。

クラスの比較

クラスオブジェクトに対しては、 比較演算子はオブジェクトの内容を比較するものです。 従って、 内容を持たない null とオブジェクトを比較するのは不正なコードです。

class C;
C c;
if (c < null)  // エラー
  ...

In 式

InExpression:
    ShiftExpression in ShiftExpression
  ShiftExpression !in ShiftExpression

連想配列では、値が配列の要素かどうかを調べることができます:

int foo[char[]];
...
if ("hello" in foo)
    ...

in 式は、 <, <=などの関係式と 同じ優先度を持ちます。 要素が配列中に存在しなければ InExpressionnull になります。存在すれば、 配列中の要素へのポインタになります。

!in 式は in 式の否定を返します。

Shift 式

ShiftExpression:
    AddExpression
    ShiftExpression << AddExpression
    ShiftExpression >> AddExpression
    ShiftExpression >>> AddExpression

オペランドは整数型でなくてはならず、 演算前に昇格が行われます。 結果は昇格後の左オペランドと同じ型を持ち、 値は左オペランドのビットを右オペランド分だけシフトした値です。

<< は左シフトです。 >> は符号付き右シフトです。 >>> は符号無し右シフトです。

シフトされる型のビット数より多くシフトしようとするのは 不正です:

int c;
c << 33;        // エラー

Add 式

AddExpression:
    MulExpression
    AddExpression + MulExpression
    AddExpression - MulExpression
    CatExpression

オペランドが整数型ならば昇格が行われます。 さらに、 共通の型へと通常の算術変換がなされます。

もしどちらか一方が浮動小数点数ならば、 もう片方も暗黙に浮動小数点数に変換され、 共通の型へと変換されます。

演算子 +- で、 第一オペランドがポインタで第二オペランドが整数型の時は、 結果の型は第一オペランドの型と同じになります。式の値は、 第一オペランドのポインタに、第二オペランドに型のサイズを掛けた結果を 足した(引いた)ポインタとなります。

第二オペランドがポインタで第一引数が整数型、そして 演算子が + の時は、 オペランドを逆転して、 上で述べた演算が行われます。

どちらのオペランドもポインタで、演算子が + の場合、これは不正です。演算子が - の場合、ポインタのアドレス値同士が減算され、 結果がオペランドの指す型のサイズで割り算されます。 二つのポインタの型が違う場合エラーです。

どちらのオペランドも整数型で、計算途中でオーバーフローやアンダーフローが発生したときは、 折り返しが発生します。つまり、 uint.max + 1 == uint.minuint.min - 1 == uint.max です。

浮動小数点数に対する加算は結合則を満たしません。

Cat 式

CatExpression:
    AddExpression ~ MulExpression

CatExpression は配列同士を結合し、 結果の動的配列を返します。結合される配列は、 同じ型の要素を持つ配列である必要があります。片方のオペランドが配列で、 もう一方がその配列の要素型だった場合は、 その要素だけを含む長さ1の配列に変換されてから、 結合が行われます。

Mul 式

MulExpression:
    UnaryExpression
    MulExpression * UnaryExpression
    MulExpression / UnaryExpression
    MulExpression % UnaryExpression

オペランドが整数型ならば昇格が行われます。 さらに、 共通の型へと通常の算術変換がなされます。

整数型の場合、*, /, % がそれぞれ 掛け算、割り算, 余り に対応しています。 掛け算ではオーバーフローは無視され、 整数型に収まる下位ビットだけが結果として残ります。

整数の /% 演算については、 商は 0 に向かって丸められ、余りは割られる数と同じ符号になります。 割る数が 0 だった場合、 例外が送出されます。

浮動小数点数では、* と / は 各演算は IEEE 754 で規定された演算に対応します。 % は IEEE754 の剰余とは一致しません。 例えば、15.0 % 10.0 == 5.0 ですが、 IEEE 754 では remainder(15.0,10.0) == -5.0 です。

浮動小数点数に対するMul式は結合則を満たしません。

単項演算式

UnaryExpression:
    & UnaryExpression
    ++ UnaryExpression
    -- UnaryExpression
    * UnaryExpression
    - UnaryExpression
    + UnaryExpression
    ! UnaryExpression
    ComplementExpression
    ( Type ) . Identifier
    NewExpression
    DeleteExpression
    CastExpression
    PowExpression

Complement 式

ComplementExpression:
    ~ UnaryExpression

ComplementExpression はboolを除く整数型に対して働き、 値中の全てのビットを反転します。

注: C や C++ と異なり、 ビット反転の前に通常の整数の昇格が行われることはありません。

New 式

NewExpression:
	new AllocatorArgumentsopt Type [ AssignExpression ]
	new AllocatorArgumentsopt Type ( ArgumentList )
	new AllocatorArgumentsopt Type
	NewAnonClassExpression

AllocatorArguments:
	( ArgumentListopt )

ArgumentList:
    AssignExpression
    AssignExpression ,
    AssignExpression , ArgumentList

NewExpression は、ガベージコレクタのヒープ(デフォルト)、 もしくはクラス/構造体特有のアロケータを使ってメモリを確保するのに使われます。

多次元配列を確保するには、 宣言は前置の型宣言と同じ順番で記述します。

char[][] foo;   // 文字列の動的配列
...
foo = new char[][30]; // 文字列 30要素の配列 を割付

上のコードは次のように書くこともできます:

foo = new char[][](30); // 30個の文字列を割り当て

多重に配列を割り当てるには、引数を複数書きます:

int[][][] bar;
...
bar = new int[][][](5,20,30);

このコードは、次と同等の意味になります。

bar = new int[][][5];
foreach (ref a; bar)
{
  a = new int[][20];
  foreach (ref b; a)
  {
    b = new int[30];
  }
}

new ( ArgumentList ) がある場合、 その引数は、 クラス/構造体特有の アロケータ関数へと、サイズ引数に続けて引き渡されます。

NewExpression が、記憶クラス scope つきの関数ローカル変数の初期化式で使用された場合で、 しかも newArgumentList が空ならば、 インスタンス用のメモリは、ヒープやクラス専用のアロケータではなく、 スタック上から割り当てられます。

Delete 式

DeleteExpression:
    delete UnaryExpression

まず、クラスオブジェクトへの参照を指定した場合で、 そのクラスにデストラクタがある場合は、 インスタンスに対してデストラクタが呼び出されます。

次に、クラスオブジェクトへの参照か構造体へのポインタを指定した場合で、 そのクラスか構造体で delete 演算子がオーバーロードされている場合、 インスタンスに対してそのdelete演算子が呼び出されます。

それ以外の場合はガベージコレクタが呼び出され、 インスタンスに割り当てたメモリは直ちに解放されます。 ガベージコレクタ以外で割り当てたメモリが使われていた場合、 動作は未定義になります。

ポインタや動的配列が指定された場合は、 ガベージコレクタが呼び出され、 メモリ領域は直ちに解放されます。 ガベージコレクタ以外で割り当てたメモリが使われていた場合、 動作は未定義になります。

ポインタ、動的配列、参照変数は、 delete のあと null にセットされます。 delete したあと他の参照でデータにアクセスしようとすると、 未定義動作となります。

スタックに割り当てられた変数が指定された場合、 クラスのデストラクタが(もしあれば)呼び出されます。 ガベージコレクタやクラス専用のアロケータは 呼び出されません。

Cast 式

CastExpression:
    cast ( Type ) UnaryExpression
    cast ( CastQual ) UnaryExpression
    cast ( ) UnaryExpression

CastQual:
    const
    const shared
    shared const
    inout
    inout shared
    shared inout
    immutable
    shared

CastExpression は、 UnaryExpression を別の Type へと変換します。

cast(foo) -p; // (-p) をfoo型にキャスト
(foo) - p;      // fooからpを引き算

クラスオブジェクトへの参照を派生クラスへとキャストするときは常に、 そのダウンキャストが適切なものであるか実行時のチェックが入ります。 不適切なキャストのときは結果は null になります。 注: これはC++で言うdynamic_cast演算子と同等の動作をしている、 と言えるでしょう。

class A { ... }
class B : A { ... }

void test(A a, B b) {
  B bx = a;         // エラー、キャストが必要
  B bx = cast(B) a; // a が B型ではない場合、bx は null となる
  A ax = b;         // キャスト不要
  A ax = cast(A) b; // no runtime check needed for upcast
}

オブジェクト o がクラス B のインスタンスであることを判定するためには、キャストを使います:

if (cast(B) o)
{
  // o は B のインスタンス
}
else
{
  // o は B のインスタンスではない
}

ポインタ型とクラス型の間のキャストは、型の塗りつぶし (つまり reinterpret_cast) として実行されます。

動的配列を別の型の動的配列にキャストするのは、 配列のlengthと要素型のsizeを掛け算した結果が一致する時に限ります。 変換は、reinterpret_cast として行われ、length は要素のsizeの比を考慮して調整されます。 長さが合わないときは、実行時エラーとなります。

import std.stdio;

int main() {
  byte[] a = [1,2,3];
  auto b = cast(int[])a; // 実行時のエラー

  int[] c = [1, 2, 3];
  auto d = cast(byte[])c; // ok
  // prints:
  // [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]
  writeln(d);
  return 0;
} 

浮動小数点数リテラルを別の型へとキャストすると型は変わりますが、 内部的には、 定数畳み込みのために、元々の精度を保って保持されます。

void test() {
  real a = 3.40483L;
  real b;
  b = 3.40483;     // リテラルはdouble精度に切り詰められない
  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);     // 初期化子が可視な場合は切り捨てられない
}

v から構造体 S へのキャストは、 v の型がSと同じではないときに限り、以下のコードと同等です:

S(v)

CastQual へのキャストは、元の UnaryExpression の型の修飾子を置き換えます。

shared int x;
assert(is(typeof(cast(const)x) == const int));

TypeCastQual も指定しないキャストは、 トップレベルの const, immutable, shared, inout 型修飾子を UnaryExpression から取り除きます。

shared int x;
assert(is(typeof(cast()x) == int));

Pow 式

PowExpression:
    PostfixExpression
    PostfixExpression ^^ UnaryExpression

PowExpression は、 左オペランドの右オペランド乗を計算します。

後置演算式

PostfixExpression:
    PrimaryExpression
    PostfixExpression . Identifier
    PostfixExpression . TemplateInstance
    PostfixExpression . NewExpression
    PostfixExpression ++
    PostfixExpression --
    PostfixExpression ( )
    PostfixExpression ( ArgumentList )
    IndexExpression
    SliceExpression

Index 式

IndexExpression:
    PostfixExpression [ ArgumentList ]

まず PostfixExpression が評価されます。 PostfixExpression が静的配列か動的配列だった場合、 シンボル $ が配列の長さとして使えます。 PostfixExpressionExpressionTuple, だった場合は、シンボル $ はタプルの要素数になります。 ArgumentList の評価の際には新しい宣言スコープが作られ、 $ はこの範囲でのみ使用できます。

PostfixExpressionExpressionTuple の場合は、 ArgumentList の要素は必ず1つで、 コンパイル時に整数定数へと評価される式でなければいけません。 その整数定数 n に応じて、 ExpressionTuple の第 n 番目の要素が選択され、 その値が IndexExpression 全体の値となります。 nExpressionTuple の範囲を外れているとエラーです。

Slice 式

SliceExpression:
    PostfixExpression [ ]
    PostfixExpression [ AssignExpression .. AssignExpression ]

まず PostfixExpression が評価されます。 PostfixExpression が静的配列か動的配列だった場合、 変数 length (と特殊変数 $) が宣言され、 その値は配列の長さになります。 AssignExpression..AssignExpression の評価の際にはこの新しい宣言スコープが作られ、 length (と $) はこの範囲でのみ使用できます。

式の結果は、 PostfixExpression の指す配列の、 先頭の AssignExpression 以上、 二番目の AssignExpression 未満、 の範囲のスライスとなります。

[ ] 形式を使うと、 スライスは配列全体になります。

スライスの型は、 PostfixExpression の要素の動的配列型となります。

SliceExpression は変更可能な lvalue ではありません。

PostfixExpressionExpressionTuple のときは、 スライス式の結果は新しく指定された上限と下限の範囲の ExpressionTuple になります。 上限と下限はコンパイル時に整数定数へと評価される式でなければなりません。 範囲の外の値が指定された場合は、 エラーになります。

原始式

PrimaryExpression:
    Identifier
    .Identifier
    TemplateInstance
    .TemplateInstance
    this
    super
    null
    true
    false
    $
    __FILE__
    __LINE__
    IntegerLiteral
    FloatLiteral
    CharacterLiteral
    StringLiterals
    ArrayLiteral
    AssocArrayLiteral
    Lambda
    FunctionLiteral
    AssertExpression
    MixinExpression
    ImportExpression
    BasicType . Identifier
    Typeof
    TypeidExpression
    IsExpression
    ( Expression )
    TraitsExpression

.Identifier

Identifier はその時点のネストされたスコープではなく、 モジュールスコープから探索されます。

this

非staticメンバ関数の中では、this はその関数を呼んだ オブジェクトへの参照となっています。 構造体での this は、 その構造体インスタンスへのポインタとなります。 typeof(this) を明示的に参照してメンバ関数を呼び出した場合は、 非仮想の関数呼び出しが行われます:

class A {
  char get() { return 'A'; }

  char foo() { return typeof(this).get(); }
  char bar() { return this.get(); }
}

class B : A {
  char get() { return 'B'; }
}

void main() {
  B b = new B();

  b.foo();  // returns 'A'
  b.bar();  // returns 'B'
}

this への代入は禁止です。

super

super は、this を基底クラスへとキャストしたものです。 基底クラスが存在しない場合はエラーになります。 また、構造体の中で super を使うのもエラーです。 (クラス Object のみが基底クラスを持ちません。) super を明示的に参照してメンバ関数を呼び出した場合は、 非仮想の関数呼び出しが行われます:

super への代入は禁止です。

null

null は、 ポインタ、関数ポインタ、デリゲート、 動的配列、連想配列、 クラスオブジェクトの null 値を表します。 特定の型にキャストされていないときは、 (void*) 型となり、 ポインタ、 関数ポインタ等々への完全一致の変換が可能です。 特定の型にキャストされた後も暗黙の変換は可能ですが、 完全一致扱いにはなりません。

true, false

bool 型の値で、 他の整数型にキャストされた場合はそれぞれ 1 と 0 になります。

文字リテラル

文字リテラルは、文字1文字を表していて、 char, wchar, dchar のいずれかの型を持ちます。 \u エスケープシーケンスならば wchar 型、 \U エスケープシーケンスならば dchar 型となります。 それ以外の場合は、 その文字を格納できる最小の型になります。

文字列リテラル

StringLiterals:
    StringLiteral
    StringLiterals StringLiteral

文字列リテラルは、 以下の型に(同じ優先度で)暗黙変換されます:

immutable(char)*
immutable(wchar)*
immutable(dchar)*
immutable(char)[]
immutable(wchar)[]
immutable(dchar)[]

文字列リテラルの末尾には 0 が必ず入っています。 これは const char* 文字列を期待する C/C++ の関数にリテラルを渡しやすくするためです。 この 0 は文字列リテラルの .length プロパティには含まれません。

配列リテラル

ArrayLiteral:
    [ ArgumentList ]

配列リテラルは、[ と ] で挟んで、カンマで区切った AssignExpression のリストです。 AssignExpression は配列の要素をあらわし、 配列の長さは要素の個数になります。 配列リテラル全体の型としては、先頭要素の型が採用されます。 ほかの要素は全て、 その型へと暗黙変換されます。 その型が静的配列型であれば、 動的配列に変換されます。

[1,2,3];  // int[3] 型。要素は 1, 2, 3
[1u,2,3]; // uint[3] 型。引数は 1u, 2u, 3u

ArgumentList の引数のどれかが ExpressionTupleであった場合は、 そのタプルの場所に引数として ExpressionTuple の要素が挿入されます。

配列リテラルはGC管理されたヒープに配置されます。 従って、安全に関数から返すことができます:

int[] foo() {
  return [1, 2, 3];
}

配列リテラルが別の配列型にキャストされた場合、 要素それぞれが新しい要素型にキャストされます。 リテラルでない配列がキャストされた場合、 配列が新しい型として再解釈され、長さが再計算されます:

import std.stdio;

void main() {
  // 配列リテラルのキャスト
  const short[] ct = cast(short[]) [cast(byte)1, 1];
  writeln(ct);  // 出力は [1, 1]

  // それ以外の配列のキャスト
  short[] rt = cast(short[]) [cast(byte)1, cast(byte)1].dup;
  writeln(rt);  // 出力は [257]
}

連想配列リテラル

AssocArrayLiteral:
    [ KeyValuePairs ]

KeyValuePairs:
    KeyValuePair
    KeyValuePair , KeyValuePairs

KeyValuePair:
    KeyExpression : ValueExpression

KeyExpression:
    AssignExpression

ValueExpression:
    AssignExpression

連想配列リテラルは、角括弧 [ と ] の間で コンマで区切られた key:value ペアのリストです。 空リストであってはいけません。 1個目のkeyの型が全体のkeyの型として使用され、 残りの key はその型へと暗黙変換されます。 1個目のvalueの型が全体のvalueの型として使用され、 残りの value はその型へと暗黙変換されます。 連想配列リテラルは、 静的初期化には使用できません

[21u:"he",38:"ho",2:"hi"]; // 型は char[2][uint] で、
                           // keyが 21u, 38u, 2u。
                           // valueが "he", "ho", "hi"

KeyValuePairs のどれかが ExpressionTupleであった場合は、 そのタプルの場所に引数として ExpressionTuple の要素が挿入されます。

ラムダ式

Lambda:
        Identifier => AssignExpression
        ParameterAttributes => AssignExpression

LambdaFunctionLiteral を簡単に書くための略記構文です。 この構文は

delegate ( Identifier ) { return AssignExpression; }

あるいは次と等価です:

delegate ParameterAttributes { return AssignExpression; }
使用例:
import std.stdio;

void main() {
	auto i=3;
	auto square = (int x) => x*x;
	auto twice  = (int x) => x*2;

	writeln(square(i));  // 9 を表示
	writeln(twice(i));   // 6 を表示
}

関数リテラル

FunctionLiteral:
        function Typeopt ParameterAttributes opt FunctionBody
        delegate Typeopt ParameterAttributes opt FunctionBody
        ParameterAttributes FunctionBody
        FunctionBody

ParameterAttributes:
    Parameters
    Parameters FunctionAttributes
FunctionLiteral によって、式の中に直接 無名関数や無名デリゲートを埋め込むことが可能になります。 Type は関数やデリゲートの返値型です。 FunctionBodyReturnStatement から推論できる場合は、 省略してもかまいません。 ( ArgumentList ) は関数の引数リストです。 省略した場合は、空の引数リスト ( ) として処理されます。 関数リテラル式の型は、関数へのポインタか デリゲートへのポインタになります。 キーワード functiondelegate が省略された場合のデフォルトは delegate です。

例として:

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;} ;
}
あるいは、次のコードは:
int abc(int delegate(long i));

void test() {
  int b = 3;
  int foo(long c) { return 6 + b; }

  abc(&foo);
}
以下と全く同等です:
int abc(int delegate(long i));

void test() {
  int b = 3;

  abc( delegate int(long c) { return 6 + b; } );
}

次の例では、返値型 int が自動推論されています:

int abc(int delegate(long i));

void test() {
  int b = 3;

  abc( (long c) { return 6 + b; } );
}
無名デリゲートは、文リテラルのように使うことができます。 例えば、以下は任意の文をループ実行する例です:
double test() {
  double d = 7.6;
  float f = 2.3;

  void loop(int k, int j, void delegate() statement) {
    for (int i = k; i < j; i++) {
      statement();
    }
  }

  loop(5, 100, { d += 1; } );
  loop(3, 10,  { f += 3; } );

  return d + f;
}
ネスト関数 と対比すると、 function を使う方は static ないしはネストしていない関数と似ていて、 delegate を使う方は、 非staticないしはネストした関数と似ています。言い換えると、 delegateリテラルは周囲の関数のスタック変数にアクセスできますが、 関数リテラルはできません。

Assert 式

AssertExpression:
    assert ( AssignExpression )
    assert ( AssignExpression , AssignExpression )

AssignExpression を評価します。 null でないクラス参照に評価された場合は、そのクラスの不変条件が実行されます。 それ以外の場合で、構造体への null でないポインタへと評価された場合は、その構造体の不変条件が実行されます。 それ以外の場合で、結果が false だった場合、 AssertError 例外が投げられます。 true ならば、 例外は投げられません。 expression で、その挙動にプログラムが依存するような副作用が発生すると、 それはエラーです。 コンパイラーはassert式を全く評価しないことも許されています。 assert 式の型は void です。 このassertは、Dの 契約プログラミング サポートの重要な一部をなしています。

assert(0) は特殊な場合です。この式は、 その部分が到達不能コードであることを示します。 到達してしまった場合、 AssertErrorが送出されるか、 プログラムが実行停止します。 (x86 プロセッサでは、実行停止には HLT 命令が使われます) コンパイラは、最適化やコード生成の段階で、 実行がassert(0)の部分には到達しないことを仮定してよいものとします。

第二引数の AssignExpression がもしあれば、 暗黙に const(char)[] へ変換できる型である必要があります。 この値は 第一引数の結果がfalseの時に評価され、 結果の文字列が AssertError のメッセージに追記されます。

void main() {
  assert(0, "an" ~ " error message");
}

これをコンパイルして実行すると、次のメッセージが表示されます:

Error: AssertError Failure test.d(3) an error message

Mixin 式

MixinExpression:
    mixin ( AssignExpression )

この AssignExpression はコンパイル時定数へと評価される文字列である必要があります。その文字列の内容は正当な AssignExpressionとしてコンパイルできるものでなければならず、 その場合、その通りにコンパイルされます。

int foo(int x) {
  return mixin("x + 1") * 7;  // ((x + 1) * 7) と同じ
}

Import 式

ImportExpression:
    import ( AssignExpression )

AssignExpression はコンパイル時定数へと評価される文字列である必要があります。 文字列の内容はファイル名として解釈されます。 その名前のファイルが読み込まれ、 ファイルの内容が文字列リテラルとなります。

処理系は、ディレクトリトラバーサル攻撃を防止するために、 importに指定できるファイル名を制限する可能性があります。 典型的な制約としては、ファイル名の中にパス区切り文字を含めない、 などがあります。

void foo() {
  // ファイル foo.txt の内容を表示
  writeln( import("foo.txt") );
}

Typeid 式

TypeidExpression:
    typeid ( Type )
    typeid ( Expression )

typeid に型 Type を指定した場合は、 その型に対応した TypeInfo クラスのインスタンスを返します。

Expression を指定した場合は、 その式の型に対応した TypeInfo クラスのインスタンスを返します。 クラス型の式であった場合、 オブジェクトの動的な型に対応した TypeInfo が返ります。 Expression は常に実行されます。

class A { }
class B : A { }

void main() {
  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
}

Is 式

IsExpression:
    is ( Type )
    is ( Type : TypeSpecialization )
    is ( Type == TypeSpecialization )
    is ( Type Identifier )
    is ( Type Identifier : TypeSpecialization )
    is ( Type Identifier == TypeSpecialization )
    is ( Type Identifier : TypeSpecialization , TemplateParameterList )
    is ( Type Identifier == TypeSpecialization , TemplateParameterList )


TypeSpecialization:
    Type
    struct
    union
    class
    interface
    enum
    function
    delegate
    super
    const
    immutable
    inout
    shared
    return
IsExpression はコンパイル時に評価され、 型の正当性を検査したり、型の同値性を比較したり、 ある型から別の型へ暗黙変換できるかどうか確かめたり、 型の部分型を推論したりするために使用します。 IsExpression の結果はint型で、条件が不成立の場合0、 成立した場合1になります。

Type はテストする型です。文法的には正しくなければなりませんが、 意味的に正しい必要は必ずしもありません。 意味的に不正な型だった場合、条件が不成立となります。

Identifier は、条件が成立した場合、結果の型への alias となります。Identifier 形式は、 が IsExpression StaticIfCondition の中に現れた時のみ使用できます。

TypeSpecializationType と比較して検査するために指定する対象の型式です。

IsExpression には以下のような種類があります:

  1. is ( Type )
    Type が意味的に正当な型の時に、条件が成立します。 (どの場合も文法的には正しい型を指定する必要があります)
    alias int func(int);    // func は関数型への alias
    void foo() {
      if (is(func[]) ) // 関数の配列は作れないので、
                            // 条件は満たされない
        writeln("satisfied");
      else
        writeln("not satisfied");
    
      if (is([][]))  // エラー。[][] は文法的に正しい型でない
        ...
    }
    
  2. is ( Type : TypeSpecialization )
    Type が意味的に正しく、 TypeSpecializationと同じかまたは暗黙に変換できる場合、 条件成立となります。 TypeSpecialization の部分には型名のみ使用できます。
    alias short bar;
    void foo(bar x) {
      if ( is(bar : int) )   // shortはintに暗黙変換されるので
                                  // 条件は満たされる
        writeln("satisfied");
      else
        writeln("not satisfied");
    }
    
  3. is ( Type == TypeSpecialization )
    Type が意味的に正当な型で、 TypeSpecialization と同じな時に条件成立です。

    If TypeSpecializationstruct union class interface enum function delegate const immutable shared のいずれかの時は、 Type が指定の種類の型であれば条件成立となります。

    alias short bar;
    
    void test(bar x) {
      if ( is(bar == int) ) // shortはintと同じ型ではないので、
                            // 条件は満たされない
        writeln("satisfied");
      else
        writeln("not satisfied");
    }
    
  4. is ( Type Identifier )
    Type が意味的に正当な型であれば条件成立です。 その場合、 IdentifierType のaliasとして宣言されます。
    alias short bar;
    void foo(bar x) {
      static if ( is(bar T) )
        alias T S;
      else
        alias long S;
    
      writeln(typeid(S));  // "short" と表示
      if ( is(bar T) )  // エラー、Identifier T の形式は
                          // StaticIfCondition にしか使えない
        ...
    }
    
  5. is ( Type Identifier : TypeSpecialization )

    TypeTypeSpecialization と同じか、または Type がクラスで TypeSpecialization がその基底クラス/インターフェイスの場合、 条件成立となります。 IdentifierTypeSpecialization への alias として宣言されるか、または TypeSpecializationIdentifier に依存しているときは、推論された型へのaliasとなります。

    alias int bar;
    alias long* abc;
    void foo(bar x, abc a) {
      static if ( is(bar T : int) )
        alias T S;
      else
        alias long S;
    
      writeln(typeid(S));  // "int" と表示
    
      static if ( is(abc U : U*) )
      {
        U u;
        writeln(typeid(typeof(u)));  // "long" と表示
      }
    }
    

    Identifier の型を決定する方法は、 TemplateTypeParameterSpecialization の時にテンプレート引数型が決定されるのと同様の方法です。

  6. is ( Type Identifier == TypeSpecialization )

    Type が意味的に正当な型で、 TypeSpecialization と同じな時に条件成立です。 IdentifierTypeSpecialization へのaliasとして宣言されるか、または、 TypeSpecializationIdentifier に依存しているときは、推論された型へのaliasとなります。

    If TypeSpecializationstruct union class interface enum function delegate const immutable shared のいずれかの時は、 Type が指定の種類の型であれば条件成立となります。 さらに、Identifier は以下の型のaliasとなります:

    キーワード Identifier の alias 先の型
    struct Type
    union Type
    class Type
    interface Type
    super 基底クラスとインターフェイスからなる TypeTuple
    enum enumの基底型
    function 関数の引数の TypeTuple。 C形式とD形式の可変個引数関数に対して使うと、 可変でない部分だけが含まれます。 型安全可変個引数では、... は無視されます。
    delegate delegate の関数型
    return 関数、delegate、関数ポインタの返値型
    const Type
    immutable Type
    shared Type
    alias short bar;
    enum E : byte { Emember }
    void foo(bar x) {
      static if ( is(bar T == int) ) // 満たされない。shortはintではない
        alias T S;
      alias T U;       // エラー、T は未定義
    
      static if ( is(E V == enum) )  // 満たされる。Eはenum
        V v;           // v はbyte型として宣言される
    }
    
  7. is ( Type Identifier : TypeSpecialization , TemplateParameterList )
    is ( Type Identifier == TypeSpecialization , TemplateParameterList )

    もっと複雑な型をパターンマッチすることも可能です。 TemplateParameterList でシンボルを宣言して、 型パターンの一部として使うことが出来ます。 マッチ規則はテンプレート引数の場合と基本的に同様です。

    import std.stdio;
    
    void main() {
      alias long[char[]] AA;
    
      static if (is(AA T : T[U], U : const char[]))
      {
        writeln(typeid(T));  // long
        writeln(typeid(U));  // const char[]
      }
    
      static if (is(AA A : A[B], B : int))
      {
        assert(0);  // Bはintではないのでマッチしない
      }
    
      static if (is(int[10] W : W[V], int V))
      {
        writeln(typeid(W));  // int
        writeln(V);          // 10
      }
    
      static if (is(int[10] X : X[Y], int Y : 5))
      {
        assert(0);  // Yは10なのでマッチしない
      }
    }
    

結合性と可換性

実装によっては、 一連の実行による結果が変化しない範囲で、 演算子の算術的な結合性と可換性の規則にしたがって 式を変形することがあります。

浮動小数点数は結合性/可換性を持たないため、 この規則に基づいた式変形の対象からは除かれます。