web-dev-qa-db-ja.com

STLコンテナーは、コンテナーがそれ自体にコピーされるときに、要素をそれ自体にコピーしないようにする必要がありますか?

問題は自己割り当てについてです。たとえば、ベクターをそれ自体にコピーします。

std::vector<std::string> vec(5, "hello");
vec = vec;

上記のコードは、文字列の5つの代入演算を実行する必要があるのでしょうか、それとも何もしませんか?次のチェックが有効かどうかを意味します。

std::vector operator=(const std::vector &rhs)
{
    if (this == &rhs)
        { return *this; }
    ...
}

私はstd::variantクラスの独自の実装に取り​​組んでおり(楽しみのために)、代入演算子の先頭に自己代入チェックを追加する必要があるのか​​、それとも含まれている要素をそれ自体にコピーする必要があるのか​​?

通常、これは問題ではないことを理解しています。自分自身にコピーするという事実を利用するクラスを作るべきではありません。しかし、規格がこれについて何か述べているかどうか興味があります。

23
anton_rh

標準で指定されたコンテナの割り当ての事前/事後条件(最新のドラフトを引用):

[tab:container.req]

r = a

確認:r == a。

これは、自己割り当てチェックを許可しますが必須ではありません。

18
eerorika

代入演算子の先頭に自己代入チェックを追加する必要があるのか​​、それとも含まれている要素をそれ自体にコピーするだけなのか、興味がありますか?

C++コアガイドライン メンバーのすべてが自己割り当て安全である場合、ユーザークラスで自己割り当てチェックを行わないことをお勧めします。

施行(単純)代入演算子には、パターンif (this == &a) return *this; ???を含めないでください

これは効率上の理由によるものです。自己割り当ては実際には起こりそうにありません。これらはまれであるため、すべての操作で自己割り当てチェックを実行しないことをお勧めします。自己割り当てチェックにより、自己割り当ての場合はコードが高速になり(非常にまれ)、他のすべての場合はコードが遅くなります(より一般的)。

100万個の要素を割り当てたとします。すべての割り当て操作で、自己割り当てチェックが行われます。そして、割り当てのどれも実際には自己割り当てではないので、それはほとんど何もせずに行われているでしょう。そして、100万の無駄なチェックを行います。

自己割り当てチェックの実行をスキップした場合、自己割り当てが実際に発生した場合にすべてのメンバーに無用の自己割り当てを実行することを除いて、問題は発生しません(割り当ての最初に単一の自己割り当てチェックを実行するよりも遅い場合があります)。オペレーター)。しかし、コードが100万回の自己割り当てを行う場合、すべての操作で自己割り当てチェックを実行するのではなく、アルゴリズムを再検討する理由になります。

ただし、デフォルトでは自己割り当てセーフではないクラスには、引き続き自己割り当てチェックを使用する必要があります。例はstd::vectorです。コピー先のベクターは、まず既存の要素を削除する必要があります。ただし、デスティネーションベクトルとソースベクトルが同じオブジェクトの場合、デスティネーションベクトルの要素を削除することにより、ソースベクトルの要素も削除します。そして、削除後にそれらをコピーすることはできません。 libstdc ++std::vectorの自己割り当てチェックを行うのはそのためです(ただし、 implementstd::vectorは自己なしで可能です)割り当てチェック)。

ただし、たとえばstd::variantの場合は機能しません。バリアントをそれ自体にコピーすると、含まれている値がそれ自体にコピーされます。 実例 を参照してください。それ自体をコピーすることは自己割り当て安全であるため(含まれている値が自己割り当て安全である場合)。

したがって、libstdc ++std::vector(自己割り当ての安全性を提供するため)の自己割り当てチェックを行い、std::variant(効率のため)は行わない。

4
anton_rh

[...]このチェックを代入演算子の先頭に追加する必要がある場合[...]?

std::vector、または他のSTLコンテナーがそれを実行するかどうかに関係なく、実行する必要があります。ライブラリを操作し、STLコンテナースコープ外でx = xを実行するユーザーを想像してください。

さて、STLコンテナの要件について-標準では、自己割り当てであるかどうかのチェックを実行するために割り当てが必要かどうかを指定していないと思います(Containers libraryセクションの大部分を通過した)。これはコンパイラの最適化の余地を与え、まともなコンパイラがそのようなチェックを実行するべきだと私は信じています。

1
Fureeish

チェックthis == &rhsは実際にはかなり よく知られたイディオム であり、lhsrhsが異なるオブジェクトであることを保証することで、何も壊さないようにするのに役立ちます。したがって、これは有効であり、実際に推奨されます。

ただし、チェックを行うためにSTLコンテナーが必要かどうかはわかりません。

0
lisyarus