web-dev-qa-db-ja.com

クラスにスワップ機能を提供する方法は?

STLアルゴリズムでswapを有効にする適切な方法は何ですか?

1)メンバーswapstd::swapはSFINAEトリックを使用してメンバーswapを使用しますか。

2)同じ名前空間内の独立したswap

3)std::swapの部分的な専門化。

4)上記のすべて。

ありがとうございました。

編集:私は質問を明確に伝えなかったようです。基本的に、テンプレートクラスがあり、そのクラス用に作成した(効率的な)スワップメソッドを使用するにはSTLアルゴが必要です。

80
pic11

1)は、swapの適切なuseです。 「ライブラリ」コードを記述し、swapでADL(引数依存ルックアップ)を有効にする場合は、このように記述します。また、これはSFINAEとは関係ありません。

// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs){
  using std::swap; // enable 'std::swap' to be found
                   // if no other 'swap' is found through ADL
  // some code ...
  swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                  // or falls back on 'std::swap'
  // more code ...
}

2)クラスにswap関数を提供する適切な方法です。

namespace Foo{

class Bar{}; // dummy

void swap(Bar& lhs, Bar& rhs){
  // ...
}

}

1)に示すようにswapが使用されると、関数が見つかります。また、どうしても必要な場合はその関数をフレンドにするか、free関数によって呼び出されるメンバーswapを提供します。

// version 1
class Bar{
public:
  friend void swap(Bar& lhs, Bar& rhs){
    // ....
  }
};

// version 2
class Bar{
public:
  void swap(Bar& other){
    // ...
  }
};

void swap(Bar& lhs, Bar& rhs){
  lhs.swap(rhs);
}

3)明示的な専門化を意味します。パーシャルはまだ別のものであり、関数では不可能であり、構造体/クラスのみです。そのため、std::swapテンプレートクラスの場合、haveを使用して、名前空間に無料の機能を提供します。私がそう言うかもしれないなら、悪いことではありません。現在、明示的な特殊化も可能ですが、一般的には 関数テンプレートを特殊化したくない

namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs){
  // ...
}

}

4)いいえ。1)は2)および3)とは異なります。また、2)と3)の両方があると、2)が選択されることになります。

90
Xeo

クラスがテンプレートクラスである可能性のあるEDITに答えるために、特別化はまったく必要ありません。次のようなクラスを考えてください:

template <class T>
struct vec3
{
    T x,y,z;
};

次のようなクラスを定義できます。

vec3<float> a;
vec3<double> b;
vec3<int> c;

1つの関数を作成して3つのスワップすべてを実装できるようにしたい場合(この例のクラスがそれを保証するわけではありません)、Xeoが(2)で言ったように...特殊化せずに、通常のテンプレート関数を作成します:

template <class T>
void swap(vec3<T> &a, vec3<T> &b)
{
    using std::swap;
    swap(a.x,b.x);
    swap(a.y,b.y);
    swap(a.z,b.z);
}

スワップテンプレート関数は、スワップしようとしているクラスと同じネームスペースに配置する必要があります。次のメソッドは、ADLを使用してその名前空間を参照していない場合でも、そのスワップを見つけて使用します。

using std::swap;
swap(a,b);
1
Ben

(2)(自立swapは、ユーザー定義クラスが宣言されている同じネームスペースでswapを提供する唯一の許可された方法のようですユーザー定義クラス。名前空間stdに宣言を追加することは、通常、未定義の動作です。 名前空間std(cppreference.com)の拡張

名前空間stdまたはstd内にネストされた名前空間に宣言または定義を追加することは未定義の動作です。ただし、以下に示すいくつかの例外があります

また、swapは、これらの例外の1つとして示されていません。したがって、独自のswapオーバーロードをstd名前空間に追加することは、未定義の動作です。

また、標準ライブラリは、ユーザー定義のswapが提供されている場合、ユーザークラスのユーザー定義swapを呼び出すために、swap関数の非修飾呼び出しを使用すると言われています。

スワップ可能(cppreference.com)

多くの標準ライブラリ関数(多くのアルゴリズムなど)は、引数がSwappableを満たすことを期待します。つまり、標準ライブラリがスワップを実行するときは常に、using std::swap; swap(t, u);

スワップ(www.cplusplus.com)

標準ライブラリの多くのコンポーネント(std内)は、swapunqualifiedの方法で呼び出して、このジェネリックの代わりに非基本型のカスタムオーバーロードを呼び出せるようにします。バージョン:提供されるタイプと同じネームスペースで宣言されたswapのカスタムオーバーロードは、この汎用バージョンで引数依存のルックアップを通じて選択されます。

ただし、ユーザー定義クラスにstd::swap関数を直接使用すると、ユーザー定義swapの代わりにstd::swapの汎用バージョンが呼び出されることに注意してください。

my::object a, b;
std::swap(a, b); // calls std::swap, not my::swap

したがって、標準ライブラリで実行されるのと同じ方法で、ユーザーコードでswap関数を呼び出すことをお勧めします。

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
0
anton_rh