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

Const と Immutable

データ構造やインターフェイスを調べるときには、 どのデータが不変でどのデータが可変で、変更を行う可能性があるのは誰か、 といった情報が簡単にわかると非常に便利です。 そしてこれは、言語の型システムの助けによって実現することができます。 データは const あるいは immutable とマーク付けすることができ、 デフォルトでは変更可能 (mutable) となっています。

immutable は、 一度構築されたら決して変更されることのないデータに適用します。 immutable なデータ値は、 プログラムの実行中一貫して同じ値であり続けます。 immutable なデータは ROM (Read Only Memory) や、ハードウェアによって読み取り専用とされたメモリページに配置することができます。 immutable なデータは決して変更されないので、 さまざまなプログラム最適化の機会をもたらします。また、 アプリケーションをより関数型のプログラミングスタイルで書くことを可能にします。

const は、そのconst参照を通しては変更を加えることのないデータに適用します。 ただし、データ自体の値は、 同じデータを指す別の参照経由で変化するかもしれません。 アプリケーション内で const を使用することで、 渡されたデータを変更せずに読み取りだけであるというインターフェイスを表現することができます。

immutable と const のどちらも、推移的 です。つまり、 immutable な参照経由でアクセスできるデータは全てimmutableですし、 const の場合にも同様のことが成り立ちます。

immutable 記憶域クラス

一番単純な immutable は、記憶域クラスとしての使い方です。 これは記号定数(manifest constant)を宣言するのに使用することができます。

immutable int x = 3;  // x は 3 になる
x = 4;        // エラー。x は immutable
char[x] s;    // s は char 3つの配列

immutable変数の型は初期化子から推論されます:

immutable y = 4; // int型のy
y = 5;           // エラー。y は immutable

初期化子を与えていない場合、 immutable を対応するコンストラクタで初期化することが可能です:

immutable int z;
void test() {
  z = 3; // エラー。z は immutable
}
static this() {
  z = 3; // ok
         // 静的初期化子のないimmutableの値は設定できる
}

非ローカルな immutable 宣言の初期化子はコンパイル時評価可能でなければなりません:

int foo(int f) { return f * 3; }
int i = 5;
immutable x = 3 * 4;      // ok, 12
immutable y = i + 1;      // エラー。コンパイル時評価不可能
immutable z = foo(2) + 1; // ok。 foo(2) はコンパイル時に7に評価される

staticでないローカルなimmutable宣言の初期化子は、 実行時に評価されます:

int foo(int f)
{
  immutable x = f + 1;  // 実行時に評価
  x = 3;                // エラー。x は immutable
}

immutable は推移的なので、 immutableから参照されているデータもまたimmutableです:

immutable char[] s = "foo";
s[0] = 'a';  // エラー。s は immutable なデータを指している
s = "bar";   // エラー。s は immutable

immutable宣言はlvalueとして使うことができます。 つまり、アドレスを取ることが可能です。

const 記憶域クラス

const 宣言は、以下の違いを除いて、 immutable とほぼ同じです:

immutable 型

immutableと修飾された型の値は、決して変更されません。 予約語 immutable は type constructor として使うことができます:

immutable(char)[] s = "hello";

immutable は、括弧の中に書かれた型に適用されます。 例えばこの例の場合、変数 s に別の値を代入することはできますが、 s[] の中身に別の値を入れることはできません:

s[0] = 'b';  // エラー。s[] は immutable
s = null;    // ok。sそのものはimmutableでない

immutable性は推移的です。つまり、immutable性は、 immutable型から参照されるすべてのものに伝搬します:

immutable(char*)** p = ...;
p = ...;        // ok, p はimmutableでない
*p = ...;       // ok, *p はimmutableでない
**p = ...;      // エラー。 **p は immutable
***p = ...;     // エラー。***p は immutable

記憶域クラスとして使われる immutable は、宣言の型全体をimmutableで修飾したのと同じ効果になります:

immutable int x = 3;   // x は immutable(int) 型
immutable(int) y = 3;  // y は immutable

immutable なデータを作る

一番単純な方法は、最初からimmutableとされているリテラルを使うことです。 例えば、文字列リテラルは immutable です。

auto s = "hello";   // s は immutable(char)[5]
char[] p = "world"; // エラー。暗黙キャストで
		    // immutableを外すことはできない

二つめの方法は、データを明示的にimmutableにキャストすることです。 この方法をとるときは、 そのデータを他に書き換えるような参照がないことをプログラマが保証する必要があります。

char[] s = ...;
immutable(char)[] p = cast(immutable)s;     // 未定義動作
immutable(char)[] p = cast(immutable)s.dup; // ok。pがs.dupへの唯一の参照

.idup ロパティを使うと、 配列のimmutableなコピーを簡単に作ることができます:

auto p = s.idup;
p[0] = ...;	  // エラー。p[] は immutable

キャストでimmutableを取り除く

immutable をキャストで除去することは可能です:

immutable int* p = ...;
int* q = cast(int*)p;

しかし、だからといって、データを書き換えられるようになるわけではありません:

*q = 3; // コンパイルは通るが、未定義動作

immutable性を取り除くキャストは、正しく静的型がついていないくて、 しかもそれが修正できないという場面で必要となってしまうことがあります。 例えば、正しく型のついていないライブラリを使う場合などです。 キャストは使い方次第で毒にも薬にもなる道具です。 コンパイラが保証するimmutable性の正しさをキャストで取り除く以上、 データの immutable 性に関してプログラマが責任を持つことが前提となります。

immutable メンバ関数

immutable メンバ関数では、 this 参照経由で参照される全てのデータがimmutableであることが保証されます。 以下のように宣言します:

struct S {   
  int x;

  void foo() immutable {
    x = 4;      // エラー。 x は immutable
    this.x = 4; // エラー。 x は immutable
  }
}

関数の左側に immutable を書くのは、返値型への修飾にはなりません:

struct S {
  immutable int[] bar()  // bar は immutable ですが、返値型はそうではありません!
  {
  }
}

返値型を immutable にするには、括弧で囲う必要があります:

struct S {
  immutable(int[]) bar()  // 今度は bar は mutable で、返値型が immutable.
  {
  }
}

メソッドと返値の両方を immutable にするには、こうします:

struct S {
  immutable(int[]) bar() immutable
  {
  }
}

const 型

const 型は immutable 型に似ていますが、const は データの読み込み専用の view を表します。 他に参照があってデータを書き換える可能性は残っています。

const メンバ関数

const メンバ関数は、 thisの指すオブジェクト自身のメンバを書き換えることが許されない メンバ関数です。

暗黙の変換

書き換え可能な型とimmutable型のデータは、constに暗黙変換できます。 書き換え可能な型をimmutableに変換したり、 その逆の変換が暗黙に行われることはありません。

Dのimmutable

const と C++のconstの比較,
const, immutable の比較
機能 D C++98
予約語 const Yes Yes
予約語 immutable Yes No
const の記法 関数的:
// const int への const ポインタ へのポインタ
const(int*)* p;
後置:
// const int への const ポインタ へのポインタ
const int *const *p;
推移的 const Yes:
// const intへのconstポインタへのconstポインタ
const int** p;
**p = 3; // エラー
No:
// intへのポインタへのconstポインタ
int** const p;
**p = 3;    // ok
const除去キャスト Yes:
// const intへのポインタ
const(int)* p;
int* q = cast(int*)p; // ok
Yes:
// const intへのポインタ
const int* p;
int* q = const_cast<int*>p; //ok
const除去後の書き換え No:
// const intへのポインタ
const(int)* p;
int* q = cast(int*)p;
*q = 3;   // 未定義動作
Yes:
// const intへのポインタ
const int* p;
int* q = const_cast<int*>p;
*q = 3;   // ok
トップレベルのconstでのoverload Yes:
void foo(int x);
void foo(const int x);  //ok
No:
void foo(int x);
void foo(const int x);  //エラー
constとmutableの別名参照 Yes:
void foo(const int* x, int* y)
{
   bar(*x); // bar(3)
   *y = 4;
   bar(*x); // bar(4)
}
...
int i = 3;
foo(&i, &i);
Yes:
void foo(const int* x, int* y)
{
   bar(*x); // bar(3)
   *y = 4;
   bar(*x); // bar(4)
}
...
int i = 3;
foo(&i, &i);
immutableとmutableの別名参照 No:
void foo(immutable int* x, int* y) {
 bar(*x); // bar(3)
 *y = 4;  // 未定義動作
 bar(*x); // bar(??)
}
...
int i = 3;
foo(cast(immutable)&i, &i);
immutable はナシ
文字列リテラルの型 immutable(char)[] const char*
文字列リテラルから非const型への暗黙変換 なし あり、ただし非推奨