コンストラクタおよびその他の特殊関数
この章では構造体のみに焦点を当てているが、ここで説明するトピックは、ほとんどの場合、クラスにも適用される。違いについては、後の章で説明する。
構造体には、その型の基本的な操作を定義する4つのメンバー関数が特別にある。
this()
コンストラクタ~this()
破壊this(ref const(S))
コピーコンストラクタ(
S
は、構造体の型を表す単なる例である。)opAssign()
代入
さらに、新しく書くコードでは使用しないことが推奨される、レガシー関数もある。
this(this)
for postblit
これらの基本的な操作は、構造体では自動的に処理される。しかし、必要に応じて、異なる実装を提供するために、手動で定義することも可能だ。
コンストラクタ
コンストラクタの役割は、メンバーに適切な値を割り当てて、オブジェクトを使用できるように準備することだ。
コンストラクタは、前の章ですでに使用している。型名が関数のように使用される場合、実際にはコンストラクタが呼び出される。これは、次の行の右側で確認できる。
同様に、次の行の右側ではクラスオブジェクトが構築されている:
括弧内に指定されている引数は、コンストラクタのパラメータに対応している。例えば、上記の8と30は、TimeOfDay
コンストラクタのパラメータとして渡されている。
これまで見てきたさまざまなオブジェクトの構築構文に加えて、const
、immutable
、およびshared
オブジェクトは、型コンストラクタ構文を使用して構築することもできる(例:immutable(S)(2)
)。(shared
キーワードについては、後の章で説明する。)
例えば、以下の3つの変数はすべてimmutable
だが、変数a
の構築は、変数b
およびc
の構築とは意味的に異なる。
コンストラクタの構文
他の関数とは異なり、コンストラクタには戻り値はない。コンストラクタの名前は、常にthis
である。
コンストラクタのパラメータには、有用で一貫性のあるオブジェクトを作成するために必要な情報が含まれる。
コンパイラが生成する自動コンストラクタ
これまで見てきた構造体はすべて、コンパイラによって自動的に生成されたコンストラクタを利用している。自動コンストラクタは、パラメータの値を、指定された順にメンバーに割り当てる。
.init
構造体の章で覚えているように、末尾のメンバの初期値は指定する必要はない。指定されていないメンバは、それぞれの型のデフォルト値で初期化される。メンバの.init
値は、そのメンバの定義で、=
演算子の後に指定することができる。
また、パラメータ数の可変の章で説明した デフォルトのパラメータ値の機能も考慮すると、次のstruct
の自動コンストラクタは、次のthis()
と同等になると考えられる。
ほとんどの構造体では、コンパイラが生成するコンストラクタで十分だ。オブジェクトを構築するには、各メンバーに適切な値を指定するだけで済む。
メンバーへのアクセスthis.
パラメーターとメンバーを混同しないように、上記のパラメーター名には_parameter
が接尾辞として付加されている。そうしないとコンパイルエラーが発生する:
理由は、c
だけではパラメーターを意味し、メンバーを意味しないため、上記のパラメーターはin
として定義されているため、変更できないからだ:
エラー: 変数deneme.Test.this.cはconstを変更できない
解決策は、メンバー名の前にthis.
を付けることだ。メンバー関数内では、this
は"このオブジェクト"を意味し、this.c
は"このオブジェクトの c メンバー"を意味する。
これで、c
はパラメータを意味し、this.c
はメンバーを意味するようになり、コードはコンパイルされ、期待どおりに動作する。メンバーc
は、パラメータc
の値で初期化される。
ユーザー定義のコンストラクタ
コンパイラが生成するコンストラクタの動作を説明した。このコンストラクタはほとんどのケースに適しているため、手動でコンストラクタを定義する必要はない。
しかし、オブジェクトの構築に、各メンバーに順番に値を代入するよりも複雑な操作が必要な場合もある。例として、前の章で説明したDuration
を考えてみよう。
この単一メンバー構造体には、コンパイラが生成するコンストラクタで十分だ。
このコンストラクタは分単位の期間を受け取るため、プログラマーは時々計算を行う必要がある:
これらの計算を不要にするため、2つのパラメーターを受け取り、自動的に計算を行うDuration
コンストラクターを設計できる:
時間と分は個別のパラメータになったため、ユーザーは自分で計算を行う必要がなく、値を指定するだけで済む。
メンバーへの最初の代入はコンストラクタ内で行われる
コンストラクタでメンバーの値を設定する場合、各メンバーへの最初の代入は特別に扱われる。そのメンバーの.init
値に新しい値を代入する代わりに、最初の代入によってそのメンバーが実際に構築される。そのメンバーへのそれ以降の代入は、通常の代入操作として扱われる。
この特別な動作は、immutable
およびconst
メンバーを、実行時にのみ知られる値で実際に構築するために必要だ。そうしないと、immutable
およびconst
変数への代入は許可されないため、これらのメンバーを望ましい値に設定することができなくなる。
次のプログラムは、immutable
メンバーに対して代入操作が一度だけ許可される方法を示している。
ユーザー定義のコンストラクタは、コンパイラによって生成されたコンストラクタを無効にする
プログラマによって定義されたコンストラクタは、コンパイラによって生成されたコンストラクタの一部を無効にする。オブジェクトは、デフォルトのパラメータ値によって構築できなくなる。例えば、単一のパラメータによってDuration
を構築しようとすると、コンパイルエラーになる。
このコンパイルエラーは、プログラマのコンストラクタが単一のパラメータを受け取らないため、コンパイラによって生成されたコンストラクタが無効になっていることが原因だ。
解決策の1つは、単一のパラメーターを取る別のコンストラクターを提供してコンストラクターをオーバーロードすることだ:
ユーザー定義のコンストラクタは、{ }
構文によるオブジェクトの構築も無効にする。
パラメーターを指定せずに初期化することは依然として有効である:
その理由は、Dでは、すべての型の.init
値がコンパイル時に知られている必要があるためだ。上記のd
の値は、Duration
の初期値と同じである。
static opCall
デフォルトコンストラクタの代わりに
すべての型の初期値はコンパイル時に知られていなければならないため、デフォルトコンストラクタを明示的に定義することはできない。
その型のオブジェクトが構築されるたびに、いくつかの情報を出力しようとする次のコンストラクタを考えてみよう。
コンパイラの出力:
エラー: 構造体用のデフォルトコンストラクタdeneme.Deneme.thisは、
@disable指定があり、本体がない場合にのみ使用できる
注釈:後の章で、クラスのデフォルトコンストラクタを定義する方法について説明する。
この問題を回避するには、パラメータのないstatic opCall()
を使用して、パラメータを指定せずにオブジェクトを構築することができる。これは、型の.init
値には影響しないことに注意しよう。
これを機能させるには、static opCall()
がその構造体型のオブジェクトを構築して返す必要がある。
main()
内のTest()
の呼び出しは、static opCall()
を実行する:
テストオブジェクトが構築されている。
static opCall()
内でTest()
を記述することはできないことに注意。その構文はstatic opCall()
を再度実行し、無限再帰を引き起こす。
出力:
テストオブジェクトが構築されている。
テストオブジェクトが構築されている。
テストオブジェクトが構築されている。
... ← 同じメッセージが繰り返さる
他のコンストラクタの呼び出し
コンストラクタは、コードの重複を避けるために他のコンストラクタを呼び出すことができる。Duration
はこの機能の有用性を示すには単純すぎるが、次の単一パラメータのコンストラクタは 2 パラメータのコンストラクタを活用している:
分値のみを受け取るコンストラクタは、時間の値として0を渡して他のコンストラクタを呼び出す。
警告:上記のDuration
のコンストラクタには設計上の欠陥がある。単一のパラメータでオブジェクトが構築される場合、意図が明確でないためだ:
ドキュメントや構造体のコードを読むことで、このパラメータが実際には"10分"を意味することを判断することは可能だが、2つのパラメータを持つコンストラクタの最初のパラメータは時間であるため、一貫性がない。
このような設計ミスはバグの原因となるため、絶対に避ける必要がある。
コンストラクタ修飾子
通常は、変更可能、const
、immutable
、およびshared
オブジェクトには同じコンストラクタが使われる。
意味的には、これらの式の右辺で構築されるオブジェクトはすべて変更可能で、変数だけが型修飾子が異なる。これらすべてに同じコンストラクタが使われる。
オブジェクトの構築
オブジェクトの構築
オブジェクトの構築
オブジェクトの構築
結果のオブジェクトの修飾子に応じて、一部のメンバーは別の方法で初期化する必要がある場合や、初期化する必要がない場合がある。例えば、immutable
オブジェクトのメンバーは、そのオブジェクトの存続期間中、変更することはできないため、変更可能なメンバーを初期化しないままにしておくと、プログラムのパフォーマンスが向上する場合がある。
修飾子が異なるオブジェクトに対しては、修飾子付きのコンストラクタを異なる形で定義することができる:
ただし、前述のように、右側の式はすべて意味的に変更可能であるため、これらのオブジェクトは、変更可能なオブジェクトのコンストラクタで構築される。
オブジェクトの構築
オブジェクトの構築 ← constコンストラクタではない
オブジェクトの構築 ← 不変コンストラクタではない
オブジェクトの構築 ← 共有コンストラクタではない
修飾子付きコンストラクタを利用するには、型コンストラクタ構文を使用する必要がある。(型コンストラクタという用語は、オブジェクトコンストラクタと混同しないように。型コンストラクタは、オブジェクトではなく型に関連している。) この構文は、修飾子と既存の型を組み合わせて、別の型を作成する。例えば、immutable(S)
は、immutable
とS
から作成された修飾子付き型である。
この場合、右側の式にあるオブジェクトは、それぞれmutable、const
、immutable
、shared
と異なる。その結果、各オブジェクトは、それに対応するコンストラクタで構築される。
オブジェクトの構築
constオブジェクトの構築
不変オブジェクトの構築
共有オブジェクトの構築
予想通り、上記の変数はすべてauto
キーワードで定義されているため、それぞれmutable、const
、immutable
、shared
と正しく推論される。
コンストラクタパラメータの不変性
不変性の章では、参照型のパラメータをconst
またはimmutable
のどちらで定義すべきかを決定するのは容易ではないことを見てきた。コンストラクタのパラメータについても同じ考慮事項が適用されるが、コンストラクタのパラメータには通常immutable
を選択した方が良い。
その理由は、パラメーターを後で使用されるメンバーに代入することが一般的だからだ。パラメーターがimmutable
でない場合、メンバーが使用されるまでに元の変数が変更されない保証はない。
ファイル名をパラメータとするコンストラクタを考えてみよう。ファイル名は、後で学生の成績を書き込むときに使用される。不変性に関する章のガイドラインに従って、より有用にするために、コンストラクタのパラメータはconst char[]
と定義されていると仮定しよう。
上記のプログラムは、学生の成績をA文字で構成されるファイル名に保存する。 "student_grades"
。そのため、コンストラクタのパラメータや参照型のメンバーは、immutable
と定義したほうがいい場合がある。これは、string
のようなエイリアスを使用することで、文字列の場合には簡単に実現できる。以下のコードは、変更が必要な構造体の部分を示している。
これで、構造体のユーザーはimmutable
文字列を指定する必要があり、その結果、ファイル名の混乱を防ぐことができる。
単一パラメータのコンストラクタによる型変換
単一パラメータのコンストラクタは、一種の型変換を提供するものと考えてもよい。コンストラクタのパラメータから、特定の構造体型のオブジェクトを生成する。例えば、次のコンストラクタは、string
からStudent
オブジェクトを生成する。
to()
また、 もこの動作cast
を変換として認識する。この例を見るために、次のsalute()
関数を考えてみよう。Student
を期待している関数にstring
パラメータを渡すと、当然、コンパイルエラーになる。
一方、以下のすべての行では、関数を呼び出す前にStudent
オブジェクトが確実に構築される。
to
とcast
は、単一パラメーターコンストラクターを利用して、一時的なStudent
オブジェクトを構築し、そのオブジェクトを引数としてsalute()
を呼び出している。
デストラクタ
デストラクタには、オブジェクトのライフタイムが終了した際に実行する必要がある操作が含まれる。
コンパイラによって生成される自動デストラクタは、すべてのメンバーのデストラクタを順番に実行する。そのため、コンストラクタと同様、ほとんどの構造体ではデストラクタを定義する必要はない。
しかし、オブジェクトのライフタイムが終了したときに、特別な操作を実行する必要がある場合もある。例えば、オブジェクトが所有するオペレーティングシステムのリソースをシステムに返す必要がある場合、別のオブジェクトのメンバ関数を呼び出す必要がある場合、ネットワーク上のどこかで実行されているサーバーに、接続が切断される旨を通知する必要がある場合などだ。
デストラクタの名前は~this
で、コンストラクタと同様、戻り値の型はない。
デストラクタは自動的に実行される
デストラクタは、構造体オブジェクトのライフタイムが終了するとすぐに実行される。(new
キーワードで構築されたオブジェクトの場合はそうではない。)
ライフタイムと基本操作の章で見たように、オブジェクトのライフタイムは、それが定義されているスコープを離れると終了する。構造体のライフタイムが終了するタイミングは、次の通りだ。
- 通常またはスローされた例外によってオブジェクトのスコープを離れたとき。
- 匿名オブジェクトは、それらが構築された式全体の最後に破棄される。
- 構造体オブジェクトのすべての構造体メンバーは、外側のオブジェクトが破棄されると破棄される。
デストラクタの例
単純なXMLドキュメントを生成するための型を設計しよう。XML要素は、山括弧で定義される。XML要素には、データや他のXML要素が含まれる。XML要素には属性も持つことができるが、ここでは無視する。
<name>
タグで開かれた要素は、必ず対応する</name>
タグで閉じられるようにする:
<class1> ← 外側のXML要素を開く <grade> ← 内側のXML要素を開く 57 ← the data </grade> ← 内側のXML要素を閉じる </class1> ← 外側のXML要素を閉じる
上記の出力を生成できる構造体は、XML要素のタグと、それを出力する際に使用するインデントを格納する2つのメンバーによって設計できる。
XML要素の開始と終了の責任をそれぞれコンストラクタとデストラクタに割り当てると、XmlElementオブジェクトのライフタイムを管理することで、希望する出力を生成することができる。例えば、コンストラクタは<tag>
を出力し、デストラクタは</tag>
を出力することができる。
以下のコンストラクタの定義は、開始タグを生成する:
indentationString()
は、次の関数だ。
この関数は、std.array
モジュールからreplicate()
を呼び出し、指定された値を指定された回数繰り返して構成される新しい文字列を作成して返す。
デストラクタはコンストラクタと同様に定義して、終了タグを生成するようにできる:
以下のテストコードは、自動コンストラクタとデストラクタの呼び出しの効果を示すものだ:
XmlElement
オブジェクトは、上記のプログラムでは3つの別々のスコープで作成されていることに注意。出力のXML要素の開始タグと終了タグは、XmlElement
のコンストラクタとデストラクタによってのみ生成される。
<classes>
<class0>
<grade>
72
</grade>
<grade>
97
</grade>
<grade>
90
</grade>
</class0>
<class1>
<grade>
77
</grade>
<grade>
87
</grade>
<grade>
56
</grade>
</class1>
</classes>
<classes>
要素は、classes
変数によって生成される。その変数はmain()
で最初に構築されるため、出力にはその構築結果が最初に含まれる。また、その変数は最後に破棄される変数でもあるため、main()
を離れる際に、その破棄のためのデストラクタ呼び出しの結果が出力に最後に含まれる。
コピーコンストラクタ
コピーコンストラクタは、既存のオブジェクトのコピーとして新しいオブジェクトを作成する。
S
が構造体型であると仮定すると、オブジェクトがコピーされる場合は次の通りである。
- 値で受け取る関数にオブジェクトを渡す場合
- 値として受け取る関数にオブジェクトを渡す場合 値として関数からオブジェクトを返す場合
- オブジェクトを明示的にコピーする
この場合、代入演算子がコピーに使用されているため、混乱が生じる可能性がある。例えば、次のコードの2行目は、
existingObject
から新しく作成されたオブジェクトa
のコピー構築だ。ここにあるキーワードauto
は、新しいオブジェクトが定義(および構築)されていることを示す。一方、コピーコンストラクタの行以降のすべての行は、
a
が既にオブジェクトとして存在しているため、代入文だ。
デフォルトでは、コピーは、オブジェクトの対応するメンバーを順番にコピーすることで、コンパイラによって自動的に処理される。次の構造体定義と、existingObject
からコピーされる変数a
があると仮定しよう。
自動コピーコンストラクタは次の手順を実行する。
a.i
をコピーするexistingObject.i
a.d
をコピーするexistingObject.d
自動動作が適さない例としては、構造体の章で定義したStudent
型がある。この型では、その型のオブジェクトのコピーに問題があった。
スライスであるため、そのstruct
のgrades
メンバーは参照型である。Student
オブジェクトをコピーすると、元のオブジェクトとコピーの両方のgrades
メンバーが、同じint
型の実際の配列要素にアクセスできるようになる。その結果、一方のオブジェクトでグレードを変更すると、もう一方のオブジェクトでもその変更が反映される。
このような混乱を避けるため、2つ目のオブジェクトのgrades
メンバーの要素は、そのオブジェクトにのみ属する独立した要素でなければならない。このような特別なコピー動作は、コピーコンストラクタで実装されている。
コンストラクタであるため、コピーコンストラクタの名前もthis
であり、戻り値の型はない。そのパラメータの型は、構造体と同じ型であり、ref
として定義する必要がある。コピーのソースオブジェクトは変更してはならないため、パラメータにはconst
(またはinout
)を指定するのが適切だ。this
キーワードを補完するために、パラメータにthat
という名前を付けて、"このオブジェクトはあのオブジェクトからコピーされている"ことを示すと便利である。
このコピーコンストラクタはメンバーを1つずつコピーし、特にgrades
の要素は.dup
でコピーされるように注意する。その結果、新しいオブジェクトは配列要素の独自のコピーを取得する。
注釈:上記の"メンバーへの最初の代入は構築である"のセクションで説明したように、これらの代入操作は実際にはメンバーのコピー構築である。
最初のオブジェクトを通じて変更を加えても、2番目のオブジェクトには影響しなくなる:
コードの可読性は低下するが、上記のコードのように構造体の型をStudent
のように繰り返す代わりに、すべての構造体に対してパラメータ型をtypeof(this)
と一般的に記述することができる。
Postblit
PostblitはDのレガシー機能で、使用は推奨されてない。新しく書くコードでは、代わりにコピーコンストラクタを使うべきだ。Postblitは下位互換性のために引き続き使用できるが、コピーコンストラクタとは互換性がない。ある型に対してpostblitが定義されている場合、コピーコンストラクタは無効になる。
Dでのオブジェクトのコピーのレガシーな方法は、2つのステップからなる:
- 既存のオブジェクトのメンバーを新しいオブジェクトにビットごとにコピーする。このステップは、ブロック転送の略でblitと呼ばれる。
- 新しいオブジェクトに追加の調整を行う。このステップはポストブリットと呼ばれる。
postblitの名前もthis
で、戻り値の型はない。他のコンストラクタと区別するために、そのパラメータリストにはキーワードthis
が含まれる。
コピーコンストラクタとの主な違いは、既存のオブジェクトのメンバは、postblitの実行が開始される時点で既に新しいオブジェクトのメンバにコピー(blitted)されている点だ。さらに、postblitは新しいオブジェクトのメンバのみを使用して実行されるため、that
オブジェクトは存在しない。そのため、必要な(そして可能な)のは、新しいオブジェクトの調整のみだ。
Student
構造体のポストブリット関数は、次のように記述できる。
代入演算子
代入は、既存のオブジェクトに新しい値を与えることだ。
代入は他の特殊操作よりも複雑だ。なぜなら、実際には2つの操作の組み合わせだからだ:
- 左側のオブジェクトを破壊する
- 右辺のオブジェクトを左辺のオブジェクトにコピーする
しかし、この2つのステップをこの順序で実行すると、コピーが成功するかどうかがわかる前に元のオブジェクトが破棄されてしまうため、リスクがある。そうしないと、コピー操作中に例外がスローされ、左側のオブジェクトが完全に破棄され、コピーも完了しないという不整合な状態になってしまう可能性がある。
そのため、コンパイラが生成する代入演算子は、次の手順を適用して安全に動作する。
- 右側のオブジェクトを一時オブジェクトにコピーする
これは、代入操作の実際のコピー作業である。左側のオブジェクトにはまだ変更がないため、このコピー操作中に例外がスローされても、左側のオブジェクトはそのまま残る。
- 左辺のオブジェクトを破壊する
これは代入操作のもう一方の部分だ。
- 一時オブジェクトを左側のオブジェクトに転送する
このステップ中または後にポストブリットやデストラクタは実行されない。その結果、左側のオブジェクトは一時オブジェクトと同等になる。
上記のステップが完了すると、一時オブジェクトは消え、右側のオブジェクトとそのコピー(つまり左側のオブジェクト)のみが残る。
コンパイラが生成する代入演算子は、ほとんどの場合に適しているが、プログラマが定義することもできる。その場合は、潜在的な例外を考慮し、例外がスローされた場合でも動作する代入演算子を記述すること。
代入演算子の構文は次の通りだ:
- 関数の名前は
opAssign
である。 - パラメータの型は、
struct
と同じだ。(コピーコンストラクタと同様、ref const(typeof(this))
でもよい。) このパラメータは、多くの場合、右辺の略語であるrhs
と名付けられる。(コピーコンストラクタと同様、that
も適切な名前だ。) - 戻り値の型は、
struct
型と同じである。 - 関数は
return this
によって終了する。
例として、代入演算子がメッセージを出力する単純なDuration
構造体を考えてみよう。
出力:
分は100から200に変更される
他の型からの代入
struct
の型とは異なる型の値を代入したい場合がある。例えば、右辺にDuration
オブジェクトを必要とする代わりに、整数から代入できると便利だ。
これは、int
をパラメーターとする別の代入演算子を定義することで可能になる:
出力:
分は100から200に変更される
分はintに置き換えられる
注釈:便利だが、異なる型を互いに代入すると、混乱やバグの原因になることがある。
メンバー関数の無効化
@disable
として宣言された関数は使用できない。
型のメンバーに適切なデフォルト値がない場合、そのデフォルトコンストラクタを無効にすることができる。例えば、次の型では、ファイル名が空であることは不適切だ。
残念ながら、コンパイラが生成するデフォルトコンストラクタは、fileName
を空に初期化してしまう:
デフォルトコンストラクタは、@disable
と宣言することで明示的に無効にすることができ、オブジェクトは他のコンストラクタのいずれかで構築されるようになる。無効にした関数には本体を指定する必要はない。
この場合、コンパイラはthis()
の呼び出しを許可しない:
エラー: コンストラクタdeneme.Archive.thisは、@disableで注釈されているため
呼び出すことができない
Archive
のオブジェクトは、他のコンストラクタのいずれか、またはその.init
値で明示的に構築する必要がある。
コピーコンストラクタ、postblit関数、および代入演算子も無効にすることができる。
コピーコンストラクタとポストブリットを無効にすると、デストラクタが1回だけ実行すべき操作を実行する場合に役立つ。このような型のオブジェクトをコピーすると、複数のコピーに対してデストラクタが実行され、バグが発生する可能性がある。
例えば、次のデストラクタは、ロギングに使用するファイルに最後の"Finishing"というメッセージを書き込むことを意図している。
プログラムの出力は、最終メッセージが複数回表示されるため、意図したとおりに動作していないことを示している:
2022-01-03 22:21:24.3143894 | 開始した |
2022-01-03 22:21:24.3144467 | main内で実行中 |
2022-01-03 22:21:24.3144628 | fooを呼び出し中 |
2022-01-03 22:21:24.3144767 | foo内で実行中 |
2022-01-03 22:21:24.3144906 | 終了中 |
2022-01-03 22:21:24.3145035 | mainに戻った |
2022-01-03 22:21:24.3145155 | 終了中 |
この問題は、複数のLogger
オブジェクトが構築され、それぞれに対してデストラクタが実行されるために発生している。意図しない"Finishing"メッセージが早期に表示される原因となっているオブジェクトは、foo
のパラメータである。このオブジェクトは、値でコピーされるため、コピーされる。
このような場合、最も簡単な解決策は、コピーと代入を完全に無効にすることだ。
Logger
がコピーできなくなったため、foo
のパラメーターを参照で受け取るように変更する必要がある:
要約
- コンストラクタ(
this
)は、オブジェクトの使用準備を行うためのものだ。コンパイラが生成するデフォルトコンストラクタは、ほとんどのケースで十分だ。 - 構造体では、デフォルトコンストラクタの動作を変更することはできない。その代わりに、
static opCall
を使用することができる。 - 単一のパラメータを持つコンストラクタは、
to
およびcast
によって型変換の際に使用できる。 - デストラクタ(
~this
)は、オブジェクトのライフタイムが終了したときに実行する必要がある操作を定義するためのものだ。 - コピーコンストラクタ(
this(ref const(typeof(this)))
)は、既存のオブジェクトからオブジェクトをコピーする方法を定義するために使用される。 - ポストブライト(
this(this)
)は新しいコードでは使用しないことが推奨される。これは、メンバーが自動的にコピーされた後にオブジェクトを調整するために使用される。 - 代入演算子(
opAssign
)は、既存のオブジェクトの値を変更するためのものだ。 - メンバー関数は、
@disable
で無効にすることができる。