を書くswap
にはいくつかの方法があり、いくつかは他よりも優れています。しかし、時間の経過とともに、単一の定義が最も効果的であることがわかりました。swap
関数の書き方について考えてみましょう。
std::vector<>
最初に、次のような単一引数のメンバー function を持つコンテナーを確認しswap
ます。
struct vector
{
void swap(vector&) { /* swap members */ }
};
当然、私たちのクラスもそうすべきですよね?まあ、そうではありません。標準ライブラリにはあらゆる種類の不要なものがあり、メンバーswap
はその 1 つです。なんで?続けましょう。
私たちがしなければならないことは、正規のものと、それを扱うためにクラスが何をする必要があるかを特定することです。そして、スワッピングの標準的な方法は withstd::swap
です。これが、メンバー関数が役に立たない理由です。一般に、それらは物事を交換する方法ではなく、の動作には関係ありませんstd::swap
。
それでは、std::swap
仕事をするためにはstd::vector<>
、 の専門化を提供する必要があります (提供する必要がありました) std::swap
。
namespace std
{
template <> // important! specialization in std is OK, overloading is UB
void swap(myclass&, myclass&)
{
// swap
}
}
この場合、それは確かに機能しますが、明らかな問題があります。関数の特殊化は部分的ではありません。つまり、これでテンプレート クラスを特殊化することはできず、特定のインスタンス化のみを行うことができます。
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
{
// swap
}
}
この方法は、場合によっては機能しますが、常に機能するわけではありません。もっと良い方法があるはずです。
がある!関数を使用して、 ADLfriend
を介して見つけることができます。
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
何かを交換したいときは、†</sup>を関連付けてからstd::swap
、非修飾呼び出しを行います。
using std::swap; // allow use of std::swap...
swap(x, y); // ...but select overloads, first
// that is, if swap(x, y) finds a better match, via ADL, it
// will use that instead; otherwise it falls back to std::swap
friend
関数とは この辺りは混乱しています。
C++ が標準化される前は、friend
関数は「フレンド名インジェクション」と呼ばれる処理を行っていました。これは、関数が周囲の名前空間に記述されているかのようにコードが動作するというものでした。たとえば、これらは標準化前と同等でした。
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
ただし、ADLが発明されたとき、これは削除されました。このfriend
関数は、ADL を介してのみ見つけることができます。フリー関数として使用したい場合は、そのように宣言する必要がありました (たとえば、this を参照してください)。しかし、見よ!問題がありました。
を使用するだけではstd::swap(x, y)
、オーバーロードは決して見つかりません。これは、明示的に「 を見てstd
、他には何もない」と言ったためです。これが、一部の人々が 2 つの関数を作成することを提案した理由です。1 つはADLを介して検出される関数として、もう 1 つは明示的なstd::
修飾を処理するためのものです。
しかし、これまで見てきたように、これはすべての場合に機能するわけではなく、最終的には厄介な問題が発生します。代わりに、慣用的なスワッピングは別のルートに行きました: クラスの仕事を提供する代わりに、上記のように、std::swap
修飾されたを使用しないことを確認するのはスワッパーの仕事swap
です。そして、人々がそれについて知っている限り、これはかなりうまくいく傾向があります. しかし、そこに問題があります。修飾されていない呼び出しを使用する必要があるのは直感的ではありません!
これを簡単にするために、Boost などの一部のライブラリboost::swap
は、 への非修飾呼び出しを行うだけの関数 をswap
、std::swap
関連付けられた名前空間として提供しました。これにより、物事が再び簡潔になりますが、それでも残念です。
C++11 では の動作に変更がないことに注意してくださいstd::swap
。これに噛まれた場合は、ここを読んでください。
要するに、メンバー関数は単なるノイズであり、特殊化は見苦しく不完全ですが、friend
関数は完全で機能します。そして、スワップするときは、関連付けられた使用または非修飾のいずれかを使用しますboost::swap
。swap
std::swap
†非公式に、関数呼び出し中に名前が考慮される場合、名前は関連付けられます。詳細については、§3.4.2 を参照してください。この場合、std::swap
通常は考慮されません。しかし、それを関連付けて ( unqualified によって考慮されるオーバーロードのセットに追加してswap
)、見つけられるようにすることができます。