l値とr値
すべての式の値は、l値またはr値のいずれかに分類される。2つを区別する簡単な方法は、l値を実際の変数(配列や連想配列の要素を含む)とし、r値を式の一時的な結果(リテラルを含む)と考えることだ。
これを実例で説明すると、以下の最初のwriteln()
式はl値のみを使用し、もう一方の式はr値のみを使用している。
r値の制限
l値と比較して、r値には次の3つの制限がある。
r値にはメモリアドレスがない
l値には参照できるメモリ位置があるが、r値にはない。
例えば、次のプログラムでは、r値式a + b
のアドレスを取得することはできない。
エラー: a + b はlvalueではない
r値には新しい値を代入できない
変更可能であれば、l値には新しい値を代入できるが、r値には代入できない。
エラー: a + b はlvalueではない
R値は関数に参照として渡すことはできない
l値は、参照によってパラメータを受け取る関数に渡すことができるが、r値は渡すことができない。
エラー: 関数deneme.incrementByTen (ref int value)は、
引数の型(int)を使用しては呼び出せない
この制限の主な理由は、ref
パラメータを取る関数は、r値が利用できなくなった後も、その参照を後で使用するために保持できるからだ。
C++などの言語とは異なり、Dでは、関数が引数を変更しない場合でも、r値を関数に渡すことはできない。
エラー: 関数deneme.print (ref const(int) value)は、
引数の型(int)を使用しては呼び出せない
l値とr値の両方を受け入れるauto ref
パラメータの使用
前の章で述べたように、関数テンプレートのauto ref
パラメータは、l値とr値の両方を受け取ることができる。
引数がl値の場合、 auto ref
は参照を意味する。一方、r値は関数に参照として渡すことができないため、引数がr値の場合、はコピーを意味する。コンパイラがこれら2つの異なるケースに対して異なるコードを生成するには、関数はテンプレートでなければならない。
テンプレートについては、後の章で説明する。ここでは、強調表示された空の括弧が、以下の定義を関数テンプレートにしていることを覚えておいてほしい。
パラメータがl値であるかr値であるかは、__traits(isRef)
をstatic if
と組み合わせて使用することで判断できる。
static if
と__traits
については、後述の条件付きコンパイルの章で説明する。
用語
"l値"および"r値"という名称は、これら2種類の値の特性を正確に表しているわけではない。最初の文字lおよびrは、代入演算子の左側および右側の式を指す、leftおよびrightから来ている。
- 変更可能であると仮定すると、l値は代入演算の左辺式になることができる。
- r値は、代入演算の左辺式にはなれない。
l値とr値は、一般的にl値もr値も代入演算子の両側に置けるため、用語として紛らわしい。