3
  1. STL コンテナー要素にはnoexcept、コピー コンストラクターとコピー代入演算子が必要ですか? できれば参考までにお願いします。
  2. そうでない場合、複数挿入中 (例: fill insert中) に例外が発生したときの STL コンテナーの状態は何ですか。

変更の傍受/拒否を許可する汎用ラッパーを作成しようとすると、問題が発生します。私が思いついた実装は、すべてのコンテナー タイプに特化していない限り (実際にはオプションではありません)、基になるコンテナーのセマンティクスを変更する可能性があります。

たとえばstd::vectorフィル インサートがあります。

void insert (iterator position, size_type n, const value_type& val);

これは、 CopyInsertableと CopyAssignable の両方value_typeである必要があります。値の型がDefaultConstructibleである必要はないことに注意してください。

編集 3 Stroustrup自身 (956 ページの表) は、複数要素の挿入がvector、deque、list、および map のすべてに対して強力な保証を持つことになっていることを示しています。完全な標準ライブラリ操作がアトミックに成功または失敗することを意味します。

編集 4ただし、保証は、関連する操作 (この場合はコピー コンストラクター) 自体が例外をスローしない場合にのみ適用されます。これはまさに私の問題です。

私が理解している限り、これにより 2 つの基本的な実装方法が残ります。

  1. 新しい要素と copy-assign のダミーエントリを作成しますval。これは、コンテナー内の既存の要素をコピーすることによってダミー要素を作成できる場合、またはDefaultConstructiblevalue_typeである場合(必須ではありません) にのみ機能します。
  2. 要素をコンテナ内のそれぞれの場所に直接コピー構築します。これは多かれ少なかれ標準的な実装のようです。

編集2 :この用語は、言語ランタイム/標準によって未定義と考えるように人々に警告しているように見えるため、この未定義の動作とは呼びません。

どちらの実装も、コピー コンストラクターまたはコピー代入演算子のいずれかが例外を発生させると、コンテナーの内容が不明なままになるようです(つまり、例外の後にコンテナーが保持する要素が明確ではありません) 。

編集 1 : これは、メモリ リークや未定義の値など、C++ ランタイムに不適切な動作があると想定しているわけではないことに注意してください。ただし、コンテナの内容は多かれ少なかれ特定されていないようです。特に、コンテナーの内容は (一貫してはいますが) 完全に変更されている可能性があります。

例として、3 番目の (ハイブリッド) メソッドを考えてみましょう。

  1. nテンプレート オブジェクトのコピーのリストを作成しますval
  2. このリストの要素をコピーしてターゲット コンテナーに割り当てます。

違いは、コピー コンストラクターが例外を発生させたときのコンテナーへの影響です。この場合、コピー コンストラクターがスローした場合、コンテナーの内容は変更されません (ただし、コピー代入演算子がスローした場合は未指定のコンテンツが発生します)。ポインターを使用する場合 (つまり、 を使用しない場合std::vector) は、コピー代入を省略してポインターのみを再配置し、操作をアトミック wrt にすることができます。例外。

コンテナ要素については、noexceptオブジェクトは を介し​​て作成されますがallocator_traits<value_type>::construct(ptr, args)、そうではなく、コンテナ要素にコピーコンストラクタ/コピー代入演算子noexceptがほとんどあるという要件も見つかりません(たとえば、これが必要です)。noexceptstd::shared_ptrstd::unique_ptr

copy-construct および copy-assign の自動生成操作はnoexcept、それ自体が例外を発生させる可能性のある操作を呼び出す必要がない限り、必要であることに注意してください。

これは私を混乱させ、何かを見逃したと確信していますが、私が間違っていることを証明するかもしれない標準の部分を理解することはできません.

4

1 に答える 1

5
  1. STL コンテナー要素には noexcept コピー コンストラクターとコピー代入演算子が必要ですか? できれば参考までにお願いします。

いいえ、そうではありません。存在しない要件の参照を提供することはできません。存在しないからです。

必要な場合は、標準でそう言うでしょうが、そうではありません。

一般に、要素はコピー コンストラクターを持つ必要さえありません。ほとんどの操作では移動コンストラクターで十分です。コピーの割り当てについても同様です。

  1. そうでない場合、フィル挿入などのマルチ挿入中に例外が発生したときの STL コンテナの状態はどうなりますか。

コンテナと、コンテナ内のどこに挿入するかによって異なります。

リストなどのノードベースのコンテナーの場合、1 つの挿入がスローされた場合、既に挿入された要素はコンテナーに残ります。

正確に何std::vectorが起こるかは、ベクター内のどこに挿入するか、再割り当てが必要かどうか、および要素に noexcept 移動コンストラクターがあるかどうかによって異なります。信頼できるのは、リークや部分的に構築された要素がないため、ベクトルが有効な状態にあるということだけです。

どちらの実装も、コピー コンストラクターまたはコピー代入演算子のいずれかが例外を発生させると、コンテナーを未定義の状態のままにするようです(つまり、例外の後にコンテナーが保持する要素が明確ではありません) 。

それは未定義の状態ではなく、完全な情報を持っていない状態です。とを使用vector::size()vector::capacity()てその状態を判断し、位置にある要素を調べて要素[0, size())の状態を確認できます。その後、ベクトルの状態に関するすべてがわかりました。

つまり、ベクトルは常に有効な状態にあります。検査するまで、その状態を正確に説明するのに十分な情報はわかりません。

「未定義」という言葉は、状態が一貫していない、または認識できないことを示唆していますが、これは正しくありません。標準コンテナーは常に基本的な例外安全性を保証するため、失敗した操作によってそれらが未定義の状態のままになることはありません。

要素型がコピー可能ではなく、投げる移動コンストラクターがあるような極端なケースでもvector::push_back()、例外はベクターを「未指定」状態のままにするため、リークや無効なオブジェクト、未定義の動作はありません。

例として、3 番目の (ハイブリッド) メソッドを考えてみましょう。

  • テンプレート オブジェクト val の n 個のコピーのリストを作成します。
  • このリストの要素をコピーしてターゲット コンテナーに割り当てます。

違いは、コピー コンストラクターが例外を発生させたときのコンテナーへの影響です。この場合、コピー代入演算子がスローしても、コンテナーの内容は変更されません。

誤解しているかもしれませんが、これが「コンテナ内のそれぞれの場所に要素を次々にコピーして構築する」よりも優れているとは思いません。N 回のコピー構築ではなく、N 回のコピー構築と N 回のコピー割り当てを行うことで、かなり多くの作業を実行できます。コンテナーの最終的な状態に関しては、何の利点もありません。

どちらの場合もn、コンテナーに新しい要素を追加する必要があります。コンテナーがスローされる可能性があります。スローされた場合、後で追加の割り当てを行うことを計画しているかどうかにかかわらず、最終状態に違いが生じる理由がわかりません。

于 2015-08-24T10:36:58.330 に答える