web-dev-qa-db-ja.com

移動のセマンティクスがあるため、std :: swapの特殊化は廃止されましたか?

重複の可能性:
セマンティクスを移動==カスタムスワップ関数は廃止されましたか?

こうやって std::swapはC++ 11のように見えます:

template<typename T>
void swap(T& x, T& y)
{
  T z = std::move(x);
    x = std::move(y);
    y = std::move(z);
}

私はまだ専門化する必要がありますかstd::swap自分のタイプの場合、またはstd::swapもちろん、私のクラスが移動コンストラクタと移動代入演算子を定義している限り、効率は最高になりますか?

35
fredoverflow

std::swapの特殊化はオプションになりましたが、非推奨ではありません。理論的根拠はパフォーマンスです。

プロトタイピングコードの場合、さらには多くの出荷コードの場合でも、std::swapは十分高速です。ただし、コードを少しずつ調べる必要がある場合でも、カスタムスワップを作成すると、パフォーマンスが大幅に向上します。

クラスに基本的に1つの所有ポインターがあり、移動コンストラクターと移動割り当てがその1つのポインターを処理する必要がある場合を考えてみます。メンバーごとにマシンのロードとストアをカウントします。

移動コンストラクタ:1つのロードと2つのストア。

移動割り当て:2つのロードと2つのストア。

カスタムスワップ:2つのロードと2つのストア。

std::swapは、1つの移動構築と2つの移動割り当て、または5つのロードと6つのストアです。

カスタムスワップは、std::swapよりも2倍または3倍高速である可能性があります。負荷とストアを数えることで何かの速度を計算しようとしているときはいつでも、どちらも速く邪悪になります。

注:移動割り当てのコストを計算するときは、移動元の値(std::swapアルゴリズム)に移動することを確認してください。これは、ブランチを犠牲にしても、割り当て解除のコストを無効にすることがよくあります。

31
Howard Hinnant

移動のセマンティクスがあるので、特殊化std::swapは廃止されましたか?

いいえ。これは汎用バージョンですが、3番目の移動操作をスキップするように最適化できます。私の好みはcopy-and-swapイディオムとクラスのstd::swapのカスタマイズを組み合わせることです。

それは私が持っていることを意味します:

class Aaaa
{
public:
    Aaaa(); // not interesting; defined elsewhere
    Aaaa(Aaaa&& rvalueRef); // same
    Aaaa(const Aaaa& ref); // same
    ~Aaaa(); // same
    Aaaa& operator=(Aaaa object) // copy&swap
    {
        swap(object);
        return *this;
    }
    void swap(Aaaa& other)
    {
        std::swap(dataMember1, other.dataMember1);
        std::swap(dataMember2, other.dataMember2);
        // ...
    }

    // ...
};

namespace std
{
    template<> inline void std::swap(Aaaa& left, Aaaa& right)
    { left.swap(right); }
}
4
utnapistim

それはあなたのタイプに依存します。

Xからz、yからx、zからyに移動します。これは、基礎となる表現の3つのコピー操作です(たった1つのポインター、おそらくそれ以上の、知っている人)。

今、あなたはあなたのタイプのためのより速いスワップを作成することができます(xorスワップトリック、インラインアセンブラ、またはおそらくあなたの基礎となるタイプのためのstd :: swapはただより速いです)。

または、コンパイラーも最適化に優れており、本質的に両方のケースを同じ命令に最適化します(レジスターにテンポラリを含めるなど)。

私は個人的には常に、移動割り当てなどのいくつかの場所から呼び出されるスワップメンバー関数を実装する傾向がありますが、YMMVです。

0
PlasmaHH

このswap()は、移動コンストラクターと2つの移動割り当てを呼び出します。私は彼の特定のタイプのクラスに対して、より効率的なswap()を書くことができると思います、

class X
{
    int * ptr_to_huge_array;
public:
// ctors, assgn ops. etc. etc.

    friend void swap(X& a, X& b)
    {
        using std::swap;
        swap(a.ptr_to_huge_array, b.ptr_to_huge_array);
    }
};

移動コンストラクタと代入演算子の実装に関係なく。

0
ali_bahoo