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


87

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

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

2)swap同じ名前空間に自立している。

3)の部分的な特殊化std::swap

4)上記のすべて。

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

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

回答:


94
  1. の適切な使用法ですswap。「ライブラリ」コードを記述し、でADL(引数依存のルックアップ)を有効にする場合は、このように記述しswapます。また、これは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 ...
}
  1. swapクラスに関数を提供する適切な方法です。
namespace Foo {

class Bar{}; // dummy

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

}

場合swap1に示すようになりました)が使用され、あなたの関数が見つかります。また、どうしても必要な場合は、その関数を友達にするか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);
}

...
  1. あなたは明示的な専門化を意味します。パーシャルはまだ別のものであり、関数では不可能であり、構造体/クラスのみです。そのstd::swapため、テンプレートクラスに特化することはできないため、名前空間に無料の関数を提供する必要があります。私がそう言うかもしれないが、悪いことではない。現在、明示的な特殊化も可能ですが、通常、関数テンプレートを特殊化する必要はありません
namespace std
{  // only allowed to extend namespace std with specializations

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

}
  1. いいえ、1)は2)および3)とは異なります。また、2)と3)の両方があると、フィットが良くなるため、常に2)が選択されることになります。

8
あなたの(1)と質問の(1)は、私が何かを読み間違えない限り、実際には並んでいません。それでも、+ 1
Dennis Zickefoose 2011年

1
@Xeo。ご入力いただきありがとうございます。質問を編集しました。ケース1で説明したように、STLはスワップを使用しますか?
pic11 2011年

1
@pic:はい、STLは1)で示したADLスワップを使用しますが、メンバー関数だけでなく、無料の関数として存在する場合に限ります。2)と3)を参照してください。どちらのバージョンも、アルゴリズムによって選択されます。3)は時代遅れであり、悪い習慣と見なされているため、2)をお勧めします。
Xeo 2011年

2
コードの最初の部分のコメントは誤解を招く可能性があります。using std::swap;ADLを有効にせず、ADLstd::swapが適切なオーバーロードを検出しないかどうかをコンパイラが検出できるようにします。
デビッド・ロドリゲス- dribeas

6
この答えは、技術的に正しいですが、ある痛んで明確にするための編集が必要に。OPの(1)は正解ではありません。この回答を読みすぎると、誤って示しているように見えるからです。
ハワードヒナント2016年

1

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

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

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

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

3つのスワップすべてを実装する1つの関数を作成できるようにしたい場合(このサンプルクラスがそれを保証するわけではありません)、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);

0

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

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

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

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

交換可能(cppreference.com)

多くの標準ライブラリ関数(たとえば、多くのアルゴリズム)は、引数がSwappableを満たすことを期待しています。つまり、標準ライブラリがスワップを実行するときはいつでも、と同等のものを使用しusing std::swap; swap(t, u);ます。

スワップ(www.cplusplus.com)

標準ライブラリ(内std)の多くのコンポーネントは、この汎用バージョンの代わりに、非基本型のカスタムオーバーロードを呼び出すことができるようswapに、修飾されていない方法で呼び出しswapます。提供されている型と同じ名前空間で宣言されたカスタムオーバーロードが選択されます通じ引数依存の検索このジェネリック版を超えます。

ただしstd::swap、ユーザー定義クラスの関数を直接使用すると、ユーザー定義のstd::swap代わりにの汎用バージョンが呼び出されることに注意してください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.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.