const(FAQ)
Dのconst/immutable システムはユニークなので、 それに関する多くの質問がある。
- なぜDにはconstがあるのか?
- D constの設計を推進した原則とは何だろうか?
- transitive constとは何か?
- head constとは何か?
- tail constとは何か?
- 論理constとは?
- なぜreadonlyを読み取り専用ビューという意味で使用しないのか?
- なぜJavaはconstを拒否したのか?
- C++ではconstはどう違うのか?
- なぜ文字列は不変なのか?
- 関数のパラメータがデフォルトで const にならないのはなぜか?
- 静的クラスのメンバーは、間接的な const の対象となるのか?
- 不変はどのような場合に有用なのか?
なぜDにはconstがあるのか?
D 2.0のconstやimmutableに対して、人々はしばしば不満を表明し、 それだけの価値があるのか疑問を呈している。
- 関数インターフェイスをより自己文書化する。 transitive const がなければ、すべてのポインタ/参照パラメータについて、 ドキュメントに頼らざるを得ない(ドキュメントが欠落していたり、古かったり、間違っている可能性もある)。 transitivity がなければ、C++のconstは、このような自己文書化にはほぼ役に立たない 。そのため、C++プログラマーは代わりに慣例に頼りがちになる。
- 信頼できるインターフェースを作成できるため、 コードに関わる人数が増えるにつれ、ますます重要になる。 言い換えれば、非常に拡張性が高いということだ。 プログラマーの大きなチームでプロジェクトに取り組む人々は、constが欠如しているために コンパイラーを頼りに規則を強制することができず、 生活が困難になっていると口を揃える。 チームが大きくなればなるほど、状況は悪化する。 大規模なプロジェクトでは、APIの管理が極めて重要である。これが、BASICがスケールしない理由である( 極端な例として)。
- 定数伝達性は、興味深い最適化の機会を生み出す。 この"値"は、これまで十分に調査も活用もされてこなかった。
- ここが重要だ。1から3までのポイントは比較すると些細なことだ。 プログラミングの未来はマルチコア、マルチスレッドになるだろう。 それらを簡単にプログラミングできる言語が、そうでない言語に取って代わるだろう。 Dをこのパラダイムに導入する鍵となるのが、推移的定数だ。 HaskellとErlangの使用が急増していることは、このトレンドが到来していることの証拠である( これらの言語の優れた特徴は、マルチプログラミングを容易にすることである)。 C++は、マルチプログラミングをサポートするような形で後付けすることはできない。 Dはまだそこまで至っていないが、いずれそうなるだろう。そして、 transitive constは、それを機能させる上で絶対に不可欠なものとなるだろう。
もちろん、シングルスレッドで、比較的小規模な開発者向けプログラムを書く場合には、 constは特に有用ではない。 また、Dではconstは使用しないことで事実上無視できる。 constが強制されるのは、不変の 文字列型のみである。
Dのconst設計の原則は何だったのか?
- それは数学的に正しいものでなければならない。つまり、 これに合法的に逃れる方法はないということだ。
- あらゆる型は構造体にラップすることができ、その結果、 その構造体は依然として同じ const 動作を示すことができる。つまり、 言い換えれば、特定の型に対して魔法のような動作はできない。
- constの振る舞いは推移的でなければならない。
- 型 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 型が変更可能なメンバーを持っているかどうかを判断する方法がない。
なぜ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システムがある が、それでも大きな違いがある。
- constは推移的ではない
- 不変値は存在しない
- constオブジェクトは変更可能なメンバーを持つことができる
- constは合法的にキャストしてデータを変更することができ
- const T T は常に異なる型ではない
なぜ文字列は不変なのか?
関数パラメータがデフォルトで const にならないのはなぜか?
ほとんどの(ほぼすべて?)関数パラメータは変更されないため、 デフォルトでそれらすべてを const にすることは理にかなっているように思える。 変更されるものは、変更可能であることを明示的にマークする必要がある。 この問題は、
- 過去のD言語の慣習や、 C、C++、Java、C#などの言語の慣習から大幅に逸脱することになる。
- 新しいキーワード、例えばmutable が必要になる。
- さらに悪いことに、宣言が不統一になってしまう。
void foo(int* p) { int* q; ... }
p constを指し、q は変更可能を指す。 このような一貫性の欠如は、さまざまなミスにつながる。 また、"型"を扱う汎用コードの記述も非常に困難になる。
in を使用することで、const への注釈付けの厄介さを軽減できる。
void str_replace(in char[] haystack, in char[] needle);
静的クラスメンバは、transitive const の対象となるか?
静的クラスメンバーはプログラムのグローバル状態の一部であり、 オブジェクトの状態の一部ではない。したがって、変更可能な静的メンバーを持つクラスは そのクラスのオブジェクトの推移的constnessを侵害することはない。
不変とは何に役立つのか?
不変のデータは、一度初期化されると、決して変更されることはない。 これは多くの用途がある。
- 不変のデータへのアクセスは、 複数のスレッドがそれを読み取る場合でも、同期する必要がない。
- データ競合、ティアリング、シーケンシャル一貫性、 キャッシュ一貫性は、すべて不変のデータを使用している場合には問題にならない。
- データ構造のディープコピーを行う場合、 "不変"の部分はコピーする必要がない。
- 不変性により、 参照渡しされた場合でも、データの大半は値型として扱うことができる (文字列が最も一般的な例である)。
- 不変型は、プログラマーに対してより多くの自己文書化情報を提供する。
- 不変のデータは、ハードウェアで保護された読み取り専用メモリに配置できる。 あるいは、ROMに配置することも可能である。
- 不変のデータが変更された場合、それはメモリ破損バグの兆候であることは確実であり、 そのようなデータの整合性を自動的に確認することが可能である。
- 不変型は、多くのプログラム最適化の機会を提供する。
constは変更可能と不変の世界の橋渡しをするので、 単一の関数で両方の型の引数を受け取ることができる。
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.109.1
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku