swap()
たとえば、次のように、その操作を読み続けます。
template<class T>
void swap (T &a, T &b)
{
T temp (a);
a = b;
b = temp;
}
Exception-safety を扱っているときに問題があります。
それの何がそんなに悪いのですか?さらに、どうすれば解決できるのでしょうか。
swap()
たとえば、次のように、その操作を読み続けます。
template<class T>
void swap (T &a, T &b)
{
T temp (a);
a = b;
b = temp;
}
Exception-safety を扱っているときに問題があります。
それの何がそんなに悪いのですか?さらに、どうすれば解決できるのでしょうか。
T
一般的な実装では、 canの操作を前提としてthrow
、例外が発生した場合に操作前とまったく同じ状態を残すことを意味する強力な例外保証を提供することはできません。の各操作T
が強力な例外保証を提供する場合でも:
template<class T>
void swap (T &a, T &b)
{
T temp (a); // [1]
a = b; // [2]
b = temp; // [3]
}
[1] がスローされた場合、入力はそのまま残されます。これは良いことです。[2] がスローされ、強力な例外が保証されていると仮定すると、値はまだ変更されていません。これは良いことです。しかし、スローするのが [3] で、a
既に変更されている場合、つまり、例外がスタックに伝播した後、呼び出し元は元の状態でも最終状態でもない状態のままになります。
編集:さらに、どうすれば解決できますか?
一般的な解決策はありません。ただし、ほとんどの場合、型に対して例外安全なswap
操作を提供できます。vector<T>
3 つのポインター ( begin
、end
、 ) を使用して状態を内部的に管理する を考えてみましょうcapacity
。上記の一般的なswap
ものはスローできます (割り当てに失敗し、内部のコンストラクターT
がスローする可能性があります...) が、スローしないswap
実装を提供するのは簡単です:
template <typename T>
class vector {
T *b,*e,*c;
public:
void swap( vector<T>& rhs ) {
using std::swap;
swap( b, rhs.b );
swap( e, rhs.e );
swap( c, rhs.c );
}
//...
};
template <typename T>
void swap( vector<T>& lhs, vector<T>& rhs ) {
lhs.swap(rhs);
}
ポインターのコピーはスローできないため、swap
上記は非スロー保証を提供し、常にswap
上記のパターンに従って実装する (using std::swap;
その後に への非修飾呼び出しが続くswap
) 場合、ADL によって よりも適切な一致として取得されstd::swap
ます。
もちろんダニエルは正しい。
ただし、例外の安全性に関しては、スワップは問題ではなく、解決策の一部 (「コピーとスワップのイディオム」) と見なされることが多いことを忘れてはなりません。残りの問題は、このイディオムを採用する場合、Daniel が説明した理由により、スワップするコピー コンストラクターが呼び出し、例外をスローしないことを確認する必要があることです。
例外の原因が説明されました
unsigned byte*
ただし、引数をキャストしてバイトごとにスワップすることで例外を回避できますsizeof(T)
。これは、自己参照が行われている場合に前提条件に違反する可能性のあるオブジェクトのコンストラクターまたはデストラクタを呼び出さないことに注意してください (スワップ後にを指します)a
b
a
a
これは、D標準ライブラリのスワップがこれを行う方法です(自己参照をチェックした後)