std::vector<T>
外部リソース(またはなど)を使用した「値型」の問題はstd::string
、それらのコピーは非常にコストがかかる傾向があり、コピーはさまざまなコンテキストで暗黙的に作成されるため、パフォーマンスの問題になる傾向があります。この問題に対するC++0xの答えは、移動セマンティクスです。これは、概念的にはリソースの盗用の概念に基づいており、技術的には右辺値参照によって強化されています。
Dには、移動セマンティクスまたは右辺値参照に似たものがありますか?
std::vector<T>
外部リソース(またはなど)を使用した「値型」の問題はstd::string
、それらのコピーは非常にコストがかかる傾向があり、コピーはさまざまなコンテキストで暗黙的に作成されるため、パフォーマンスの問題になる傾向があります。この問題に対するC++0xの答えは、移動セマンティクスです。これは、概念的にはリソースの盗用の概念に基づいており、技術的には右辺値参照によって強化されています。
Dには、移動セマンティクスまたは右辺値参照に似たものがありますか?
Dには、Dがそれらを移動させることができる場所(構造体を返すなど)がいくつかあると思いますが、C++はそれらをコピーします。IIRCの場合、コンパイラーは、コピーが不要であると判断できる場合は、コピーではなく移動を実行するため、構造体のコピーはC++よりもDの方が少なくなります。そしてもちろん、クラスは参照であるため、まったく問題はありません。
ただし、それにもかかわらず、コピーの構築は、DとC++ではすでに異なる動作をします。通常、コピーコンストラクターを宣言する代わりに、ポストブリットコンストラクターを宣言しますthis(this)
。呼び出される前に完全なmemcpythis(this)
を実行し、完全に新しいコンストラクターを作成するのではなく、新しい構造体が元の構造体から分離されていることを確認するために必要な変更のみを行います(必要に応じてメンバー変数のディープコピーを実行するなど)。すべてをコピーする必要があります。したがって、一般的なアプローチはすでにC++とは少し異なります。また、構造体には高価なpostblitコンストラクターを使用しないでください(構造体のコピーは安価である必要があります)。したがって、C++の場合よりも問題は少なくなります。コピーに費用がかかるオブジェクトは、通常、参照またはCOWセマンティクスを持つクラスまたは構造体のいずれかです。
コンテナは一般に参照型です(Phobosでは、ポリモーフィズムを必要としないため、クラスではなく構造体ですが、コンテナをコピーしても内容はコピーされないため、引き続き参照型です)。したがって、コンテナをコピーするのは費用がかかりません。 C++のように。
Dには、moveコンストラクターに似たものを使用できる場合がありますが、一般に、Dは、C ++がオブジェクトをコピーする際の問題を軽減するように設計されているため、問題にはほど遠いです。それがC++であること。
すべての答えが元の質問に完全に答えることができなかったと思います。
まず、前述のように、質問は構造体にのみ関連します。クラスには意味のある動きはありません。また、上記のように、構造体の場合、特定の条件下でコンパイラによって特定の量の移動が自動的に行われます。
移動操作を制御したい場合は、次のことを行う必要があります。this(this)に@disableの注釈を付けることで、コピーを無効にできます。constructor(constructor &&that)
次に、を定義することでC++をオーバーライドできますthis(Struct that)
。同様に、割り当てを。でオーバーライドできますopAssign(Struct that)
。どちらの場合も、の値を必ず破棄する必要がありますthat
。
割り当ての場合、の古い値も破棄する必要があるためthis
、最も簡単な方法はそれらを交換することです。したがって、 C++の実装は次のunique_ptr
ようになります。
struct UniquePtr(T) {
private T* ptr = null;
@disable this(this); // This disables both copy construction and opAssign
// The obvious constructor, destructor and accessor
this(T* ptr) {
if(ptr !is null)
this.ptr = ptr;
}
~this() {
freeMemory(ptr);
}
inout(T)* get() inout {
return ptr;
}
// Move operations
this(UniquePtr!T that) {
this.ptr = that.ptr;
that.ptr = null;
}
ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that"
swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary
return this;
}
}
編集:私が定義しなかったことに注意してくださいopAssign(ref UniquePtr!T that)
。これがコピー代入演算子です。これを定義しようとすると、その@disable
ようなことはないと宣言したため、コンパイラはエラーになります。
Dには、個別の値とオブジェクトのセマンティクスがあります。
struct
、デフォルトで値セマンティックになりますclass
と、オブジェクトセマンティックになります。ここで、メモリを自分で管理しないと仮定すると、Dのデフォルトの場合(ガベージコレクタを使用)、として宣言された型のオブジェクトclass
が自動的に実際のポインタ(または必要に応じて「参照」)になることを理解する必要がありますオブジェクト自体ではなく、オブジェクト。
したがって、Dでベクトルを渡す場合、渡すのは参照/ポインタです。自動的。関係するコピーはありません(参照のコピーを除く)。
そのため、D、C#、Java、およびその他の言語は、移動セマンティックを「必要」としません(ほとんどのタイプはオブジェクトセマンティックであり、コピーではなく参照によって操作されるため)。
多分彼らはそれを実装することができたでしょう、私にはわかりません。しかし、C ++のように、実際にパフォーマンスが向上するでしょうか。本質的に、それはありそうもないようです。
私はどういうわけか、実際には右辺値の参照と「移動セマンティクス」の概念全体が、ローカルの「一時的な」スタックオブジェクトを作成するのがC++では通常の結果であると感じています。DおよびほとんどのGC言語では、ヒープ上にオブジェクトを配置するのが最も一般的です。その後、一時オブジェクトを呼び出しスタックを介して返すときに、一時オブジェクトを数回コピー(または移動)してもオーバーヘッドが発生しないため、そのオーバーヘッドも避けてください。
D(およびほとんどのGC言語)では、class
オブジェクトが暗黙的にコピーされることはなく、ほとんどの場合、参照を渡すだけなので、これは、オブジェクトの右辺値参照が必要ないことを意味する場合があります。
OTOH、struct
オブジェクトは「リソースへのハンドル」ではなく、組み込み型と同様に動作する単純な値型であると想定されています。したがって、ここでも、移動セマンティクスの理由はありません、IMHO。
これは結論を導きます-Dはそれらを必要としないので右辺値参照を持っていません。
ただし、実際には右辺値参照を使用したことはなく、読んだだけなので、この機能の実際の使用例をいくつかスキップした可能性があります。この投稿は、信頼できる判断としてではなく、あなたにとって役立つことを願っている問題についての一連の考えとして扱ってください。
リソースを失うためにソースが必要な場合は、問題が発生する可能性があると思います。ただし、GCを使用すると、複数の所有者について心配する必要がなくなることが多いため、ほとんどの場合、問題にはならない可能性があります。