英語版
このページの英語版を見る

const(FAQ)

Dのconst/immutable システムはユニークなので、 それに関する多くの質問がある。


なぜDにはconstがあるのか?

D 2.0のconstやimmutableに対して、人々はしばしば不満を表明し、 それだけの価値があるのか疑問を呈している。

  1. 関数インターフェイスをより自己文書化する。 transitive const がなければ、すべてのポインタ/参照パラメータについて、 ドキュメントに頼らざるを得ない(ドキュメントが欠落していたり、古かったり、間違っている可能性もある)。 transitivity がなければ、C++のconstは、このような自己文書化にはほぼ役に立たない 。そのため、C++プログラマーは代わりに慣例に頼りがちになる。
  2. 信頼できるインターフェースを作成できるため、 コードに関わる人数が増えるにつれ、ますます重要になる。 言い換えれば、非常に拡張性が高いということだ。 プログラマーの大きなチームでプロジェクトに取り組む人々は、constが欠如しているために コンパイラーを頼りに規則を強制することができず、 生活が困難になっていると口を揃える。 チームが大きくなればなるほど、状況は悪化する。 大規模なプロジェクトでは、APIの管理が極めて重要である。これが、BASICがスケールしない理由である( 極端な例として)。
  3. 定数伝達性は、興味深い最適化の機会を生み出す。 この"値"は、これまで十分に調査も活用もされてこなかった。
  4. ここが重要だ。1から3までのポイントは比較すると些細なことだ。 プログラミングの未来はマルチコア、マルチスレッドになるだろう。 それらを簡単にプログラミングできる言語が、そうでない言語に取って代わるだろう。 Dをこのパラダイムに導入する鍵となるのが、推移的定数だ。 HaskellとErlangの使用が急増していることは、このトレンドが到来していることの証拠である( これらの言語の優れた特徴は、マルチプログラミングを容易にすることである)。 C++は、マルチプログラミングをサポートするような形で後付けすることはできない。 Dはまだそこまで至っていないが、いずれそうなるだろう。そして、 transitive constは、それを機能させる上で絶対に不可欠なものとなるだろう。

もちろん、シングルスレッドで、比較的小規模な開発者向けプログラムを書く場合には、 constは特に有用ではない。 また、Dではconstは使用しないことで事実上無視できる。 constが強制されるのは、不変の 文字列型のみである。


Dのconst設計の原則は何だったのか?

  1. それは数学的に正しいものでなければならない。つまり、 これに合法的に逃れる方法はないということだ。
  2. あらゆる型は構造体にラップすることができ、その結果、 その構造体は依然として同じ const 動作を示すことができる。つまり、 言い換えれば、特定の型に対して魔法のような動作はできない。
  3. constの振る舞いは推移的でなければならない。
  4. 型 T の const 動作は、すべての型 T に対して等価でなければならない。

推移的定数とは?

「推移的 const」とは、いったん const が型に適用されると、 その型のすべてのサブコンポーネントに再帰的に適用されることを意味する。したがって、

const(int*)** p;
p += 1;   // OK、pは変更可能である
*p += 1;  // OK、*pは変更可能である
**p += 1; // エラー、**pはconst
***p += 1; // エラー、***pはconst

推移性により、 変更可能なintへのconstポインタを持つことはできない。

C++のconstは推移的ではない。


head constとは何だろうか?

head constとは、constが適用されるのは、その型でconstに隣接するコンポーネントのみであることを意味する。 例えば、

headconst(int**) p;

p は、変更可能なポインタへの変更可能なポインタへの const ポインタであると解釈される。 D には head const はない(headconst は 説明目的で存在しているだけである)が、C++ const は head const システムである。


テール const とは何だろうか?

Tail constはhead constの補集合であり、 const型から到達可能なものはすべて、トップレベルを除いてconstとなる。 たとえば、

tailconst(int**) p;

p は、変更可能な const へのポインタであると解釈される。 ヘッド const とテール const を組み合わせると、推移的 const が得られる。 D にはtailconst (このキーワードは説明用にのみ存在する


論理的なconstとは何か?

論理的な constとは、オブザーバには定数のように見えるが 実際には定数ではないデータを指す。例としては、 遅延評価を行うオブジェクトが挙げられる。

struct Foo
{
    mutable int len;
    mutable bool len_done;
    const char* str;
    int length()
    {
        if (!len_done)
        {
            len = strlen(str);
            len_done = true;
        }
        return len;
    }
    this(char* str) { this.str = str; }
}
const Foo f = Foo("hello");
bar(f.length);

この例では、f.len は必要な場合にのみ評価される。Foo は論理的に const である。オブジェクトの観察者にとっては、 そのオブジェクトの戻り値は構築後に変更されることはないからだ。mutable 修飾子は、Foo のインスタンスが const であっても、それらのフィールドは変更可能であることを示している。 C++ は論理的な const の概念をサポートしているが、D はサポートしていない。 また、D にはmutable 修飾子もない。

論理 const の問題は、const がもはや推移的ではないことである。 推移的ではないということは、 スレッドの競合状態が発生する可能性があることを意味し、 不透明な const 型が変更可能なメンバーを持っているかどうかを判断する方法がない。

参照: 変更可能: ビット単位 vs. 論理 const


なぜreadonly を「読み取り専用ビュー」という意味で使わないのか?

readonlyという用語は、ソフトウェアの世界では、 ROM(Read Only Memory)すなわち変更できないメモリを意味する用語として定着している。 メモリページをハードウェアで保護するコンピュータでは、readonlyは メモリの内容が変更できないことも意味する。 Dでreadonlyを使用して、別のエイリアスやスレッドによって変更される可能性があるメモリの読み取り専用ビューを意味するのは、


なぜJavaはconstを拒絶したのか?

https://bugs.java.com/bugdatabase/view_bug?bug_id=4211070


constはC++ではどのように異なるのか?

C++には、他のどの言語よりもD言語に近いconstシステムがある が、それでも大きな違いがある。

  1. constは推移的ではない
  2. 不変値は存在しない
  3. constオブジェクトは変更可能なメンバーを持つことができる
  4. constは合法的にキャストしてデータを変更することができ
  5. const T T は常に異なる型ではない

なぜ文字列は不変なのか?

不変の文字列


関数パラメータがデフォルトで const にならないのはなぜか?

ほとんどの(ほぼすべて?)関数パラメータは変更されないため、 デフォルトでそれらすべてを const にすることは理にかなっているように思える。 変更されるものは、変更可能であることを明示的にマークする必要がある。 この問題は、

  1. 過去のD言語の慣習や、 C、C++、Java、C#などの言語の慣習から大幅に逸脱することになる。
  2. 新しいキーワード、例えばmutable が必要になる。
  3. さらに悪いことに、宣言が不統一になってしまう。
    void foo(int* p)
    {
        int* q;
        ...
    }
    
    p constを指し、q は変更可能を指す。 このような一貫性の欠如は、さまざまなミスにつながる。 また、"型"を扱う汎用コードの記述も非常に困難になる。

in を使用することで、const への注釈付けの厄介さを軽減できる。

void str_replace(in char[] haystack, in char[] needle);

静的クラスメンバは、transitive const の対象となるか?

静的クラスメンバーはプログラムのグローバル状態の一部であり、 オブジェクトの状態の一部ではない。したがって、変更可能な静的メンバーを持つクラスは そのクラスのオブジェクトの推移的constnessを侵害することはない。


不変とは何に役立つのか?

不変のデータは、一度初期化されると、決して変更されることはない。 これは多くの用途がある。

constは変更可能と不変の世界の橋渡しをするので、 単一の関数で両方の型の引数を受け取ることができる。