web-dev-qa-db-ja.com

それで、unique_ptrはstlコレクションで安全に使用できますか?

Unique_ptrおよびrvalue moveの哲学と混同されています。

2つのコレクションがあるとします。

std::vector<std::auto_ptr<int>> autoCollection;
std::vector<std::unique_ptr<int>> uniqueCollection;

アルゴリズムが内部で何をしているのかわからないため、内部ピボットコピーなどを作成し、auto_ptrから所有権を取り除いているため、今度は以下が失敗すると予想します。

std::sort(autoCollection.begin(), autoCollection.end());

わかったそして、コンパイラーは、これが起こることを正しく拒否します。

しかし、私はこれを行います:

std::sort(uniqueCollection.begin(), uniqueCollection.end());

そして、これはコンパイルされます。そして、私はその理由を理解していません。 unique_ptrsをコピーできるとは思いませんでした。これはピボット値を取得できないことを意味するので、並べ替えの効率が低下しますか?または、このピボットは実際には移動であり、実際にはauto_ptrsのコレクションと同じくらい危険であり、コンパイラーによって禁止されるべきですか?

重要な情報が欠けていると思うので、誰かが私にahaを提供するのを心待ちにしています!瞬間。

44
DanDan

技術よりも哲学の問題だと思います:)

根本的な問題は、移動とコピーの違いは何ですか。技術/標準化言語に飛び込むつもりはありません。簡単にやってみましょう。

  • コピー:同じオブジェクトをもう1つ作成します(または少なくとも、同等に比較する必要があります)
  • 移動:オブジェクトを取り、別の場所に置きます

あなたが言ったように、コピーの観点から移動を実装することは可能です:新しい場所にコピーを作成し、オリジナルを破棄します。ただし、そこには2つの問題があります。 1つはパフォーマンスに関するもので、もう1つはRAIIに使用されるオブジェクトに関するものです。2つのうちどちらが所有権を持つべきですか。

適切なMoveコンストラクターは、2つの問題を解決します。

  • オリジナルが破棄されるため、どのオブジェクトが所有権を持っているかは明らかです:新しいオブジェクト
  • したがって、ポイントされたリソースをコピーする必要がないため、効率が向上します。

auto_ptrunique_ptrは、このことを非常によく表しています。

auto_ptrを使用すると、コピーのセマンティックが台無しになります。元のコピーとコピーは等しくありません。 Moveセマンティクスに使用することもできますが、どこかを指すオブジェクトが失われる可能性があります。

一方、unique_ptrはまさにそのとおりです。これにより、リソースの一意の所有者が保証されるため、コピーおよびそれに続く避けられない削除の問題が回避されます。そして、コピーなしもコンパイル時に保証されます。したがって、コピーの初期化を行わない限り、コンテナに適しています。

typedef std::unique_ptr<int> unique_t;
typedef std::vector< unique_t > vector_t;

vector_t vec1;                           // fine
vector_t vec2(5, unique_t(new Foo));     // Error (Copy)
vector_t vec3(vec1.begin(), vec1.end()); // Error (Copy)
vector_t vec3(make_move_iterator(vec1.begin()), make_move_iterator(vec1.end()));
    // Courtesy of sehe

std::sort(vec1.begin(), vec1.end()); // fine, because using Move Assignment Operator

std::copy(vec1.begin(), vec1.end(), std::back_inserter(vec2)); // Error (copy)

したがって、あなたはcanコンテナでunique_ptrを使用します(auto_ptrとは異なります)。ただし、タイプがサポートしていないコピーを伴うため、いくつかの操作は不可能です。

残念ながら、Visual Studioは標準の施行がかなり緩やかで、コードの移植性を確保するために無効にする必要のある拡張機能もいくつかあります。標準のチェックに使用しないでください:)

53
Matthieu M.

unique_ptrsは、移動コンストラクタを使用して移動されています。 unique_ptrはMovableですが、CopyConstructableではありません。

右辺値参照についての優れた記事 here があります。それらについてまだ読んでいない、または混乱している場合は、ご覧ください!

13
rlbond

std::sortは、各オブジェクトのライブコピーが常に1つしかない限り、移動操作でのみ機能し、コピーは機能しません。原則として、一時的に別の配列を割り当て、すべてのオブジェクトを並べ替えるときにそれらをに移動できるため、これはインプレースでの作業よりも弱い要件です。

たとえばstd::vector<std::unique_ptr<T>>容量を超えると、より大きなベクトルにストレージを割り当て、すべてのオブジェクトを古いストレージから新しいストレージに移動します。これはインプレース操作ではありませんが、完全に有効です。

結局のところ、クイックソートやヒープソートなどのソートアルゴリズムは、実際には問題なくインプレースで機能します。クイックソートのパーティションルーチンは、std :: swapを内部的に使用します。これは、関係する両方のオブジェクトの移動操作としてカウントされます。ピボットを選択するときの1つの秘訣は、範囲内の最初の要素とピボットを交換することです。これにより、分割が完了するまで移動されません。

7
yonil