STLアルゴリズムでswap
を有効にする適切な方法は何ですか?
1)メンバーswap
。 std::swap
はSFINAEトリックを使用してメンバーswap
を使用しますか。
2)同じ名前空間内の独立したswap
。
3)std::swap
の部分的な専門化。
4)上記のすべて。
ありがとうございました。
編集:私は質問を明確に伝えなかったようです。基本的に、テンプレートクラスがあり、そのクラス用に作成した(効率的な)スワップメソッドを使用するにはSTLアルゴが必要です。
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)が選択されることになります。
クラスがテンプレートクラスである可能性のある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);
(2)(自立swap
は、ユーザー定義クラスが宣言されている同じネームスペースで)swap
を提供する唯一の許可された方法のようですユーザー定義クラス。名前空間std
に宣言を追加することは、通常、未定義の動作です。 名前空間std(cppreference.com)の拡張 :
名前空間
std
またはstd
内にネストされた名前空間に宣言または定義を追加することは未定義の動作です。ただし、以下に示すいくつかの例外があります
また、swap
は、これらの例外の1つとして示されていません。したがって、独自のswap
オーバーロードをstd
名前空間に追加することは、未定義の動作です。
また、標準ライブラリは、ユーザー定義のswap
が提供されている場合、ユーザークラスのユーザー定義swap
を呼び出すために、swap
関数の非修飾呼び出しを使用すると言われています。
多くの標準ライブラリ関数(多くのアルゴリズムなど)は、引数がSwappableを満たすことを期待します。つまり、標準ライブラリがスワップを実行するときは常に、
using std::swap; swap(t, u);
。
標準ライブラリの多くのコンポーネント(
std
内)は、swap
をunqualifiedの方法で呼び出して、このジェネリックの代わりに非基本型のカスタムオーバーロードを呼び出せるようにします。バージョン:提供されるタイプと同じネームスペースで宣言された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.