web-dev-qa-db-ja.com

標準ライブラリはstd :: swapをどのように実装しますか?

スワップ機能はSTLにどのように実装されていますか?これと同じくらい簡単ですか:

template<typename T> void swap(T& t1, T& t2) {
    T tmp(t1);
    t1=t2;
    t2=tmp;
}

他の投稿では、この関数を独自のクラスに特化することについて話しています。なぜこれを行う必要があるのですか? std::swap関数を使用できないのはなぜですか?

56

std::swapはどのように実装されますか?

はい、質問で提示された実装は、古典的なC++ 03のものです。

std::swapのより現代的な(C++ 11)実装は次のようになります。

template<typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1); // or T temp(std::move(t1));
    t1 = std::move(t2);
    t2 = std::move(temp);
}

これは、不要なコピーなどを防ぐため、リソース管理の点で従来のC++ 03実装よりも改善されています。C++ 11 std::swap では、タイプTMoveConstructible および MoveAssignable 、したがって、実装と改善が可能になります。

カスタム実装を提供する必要があるのはなぜですか?

特定のタイプのswapのカスタム実装は、通常、実装が標準バージョンよりも効率的または特定の場合に推奨されます。

この典型的な例は、コピーしてから削除するのに費用がかかる大量のリソースをクラスが管理する場合です。代わりに、カスタム実装は、スワップを実行するために必要なハンドルまたはポインターを単に交換することができます。

std::moveと可動型(およびそのように型を実装する)の出現により、C++ 11以降では、ここでの元の理論的根拠の多くが失われ始めています。ただし、カスタムスワップが標準のスワップよりも優れている場合は、それを実装します。

一般的なコードは、 [〜#〜] adl [〜#〜] メカニズムを適切に使用する場合、カスタムswapを使用できます。

57
Niall

スワップ機能はSTLにどのように実装されていますか?

どの実装ですか?これは仕様であり、単一の具体的なライブラリではありません。 私のコンパイラの標準ライブラリがそれを行う方法を意味する場合、どのコンパイラであるかを教えてもらうか、自分でコードを読んでください。

これと同じくらい簡単ですか:

これは基本的に、C++ 11より前の単純なバージョンです。

この特殊化されていない実装はコピーを強制します。例のT = std::vector<SomethingExpensive>の場合、コードは次のように変換されます。

template<typename T> void swap(T& t1, T& t2) {
  T tmp(t1); // duplicate t1, making an expensive copy of each element
  t1=t2;     // discard the original contents of t1,
             // and replace them with an expensive duplicate of t2
  t2=tmp;    // discard the original contents of t2,
             // and replace them with an expensive duplicate of tmp
}            // implicitly destroy the expensive temporary copy of t1

したがって、2つのベクトルを交換するには、基本的にthreeを作成しました。 3つの動的割り当てとコピーされた多くの高価なオブジェクトがあり、これらの操作のいずれかがスローされ、引数が不確定な状態のままになる可能性がありました。

これは明らかにひどいため、高価なコンテナにはオーバーロードが用意されており、独自の高価なタイプのオーバーロードを記述することをお勧めします。 std::vectorスペシャライゼーションはベクターの内部にアクセスでき、すべてのコピーなしで2つのベクターを交換できました。

template <typename T> void swap(vector<T> &v1, vector<T> &v2) { v1.swap(v2); }
template <typename T> void vector<T>::swap(vector<T>& other) {
  swap(this->size_, other.size_); // cheap integer swap of allocated count
  swap(this->used_, other.used_); // cheap integer swap of used count
  swap(this->data__, other.data_); // cheap pointer swap of data ptr
}

これには高価なものは一切含まれず、動的な(割り当て解除)割り当ても行われず、スローしないことが保証されていることに注意してください。

さて、この特殊化の理由は、vector :: swapがベクターの内部にアクセスし、コピーせずに安全かつ効率的に移動できることです。

なぜこれを行う必要があるのですか[あなた自身のクラスのために...]

std::vectorと同じ理由で、C++ 11より前-スワップを効率的で例外に対して安全にするため。

C++ 11以降、実際にはありません-移動の構築と割り当てを提供する場合、またはコンパイラが正常なデフォルトを生成できる場合。

新しい汎用スワップ:

template <typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1);
    t1 = std::move(t2);
    t2 = std::move(temp);
}

移動構築/割り当てを使用して、上記のカスタムベクトル実装と基本的に同じ動作を取得できます。カスタム実装をまったく作成する必要はありません。

18
Useless