を書くにはいくつかの方法がありますswap
。しかし、時間の経過とともに、単一の定義が最もうまく機能することがわかりました。swap
関数の記述についてどのように考えるかを考えてみましょう。
最初に、のようなコンテナには次のようなstd::vector<>
単一引数のメンバー関数があることがわかりますswap
。
struct vector
{
void swap(vector&) { /* swap members */ }
};
当然、私たちのクラスも当然でしょうか?まあ、そうでもない。標準ライブラリにはあらゆる種類の不要なものがあり、メンバーswap
もその1つです。どうして?続けましょう。
私たちがすべきことは、標準的なもの、およびそれを扱うためにクラスが何をする必要があるかを特定することです。そして、スワッピングの標準的な方法はstd::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
}
}
この方法は時々機能しますが、常に機能するわけではありません。もっと良い方法があるはずです。
有る!friend
関数を使用して、ADLでそれを見つけることができます。
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
何かを交換したいときは、† を関連付け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を介してのみ見つけることができます。これをフリー関数として使用したい場合は、そのように宣言する必要があります(たとえば、これを参照してください)。しかし、lo!問題が発生しました。
あなただけ使用する場合はstd::swap(x, y)
、あなたのオーバーロードはなります決して「で見て、あなたが明示的に言ってきたので、見つかりませんstd
どこにも、そして」!これが、一部の人々が2つの関数の記述を提案した理由です。1つはADLを介して検出される関数として、もう1つは明示的なstd::
修飾を処理するためのものです。
しかし、私たちが見たように、これはすべての場合に機能するわけではなく、醜い混乱に終わります。代わりに、慣用的なスワッピングは別の方法で行われました。クラスの仕事を提供するのstd::swap
ではなくswap
、上記のように修飾子を使用しないようにすることはスワッパーの仕事です。そして、人々がそれについて知っている限り、これはかなりうまくいく傾向があります。しかし、そこには問題があります。修飾されていない呼び出しを使用する必要があるのは直感的ではありません!
これを簡単にするために、ブーストのようないくつかのライブラリが機能提供boost::swap
だけに修飾されていない呼び出しを行う、swap
と、std::swap
関連付けられた名前空間としての。これは、物事を簡潔にするのに役立ちますが、それでもなお厄介です。
C ++ 11ではの動作に変更はないことに注意してくださいstd::swap
。これは、私や他の人が誤っていると考えていた動作です。これに気づかれた場合は、こちらをお読みください。
つまり、メンバー関数は単なるノイズであり、特殊化は醜く不完全ですが、friend
関数は完全で機能します。そして、あなたは交換したときに、使用のいずれかboost::swap
または修飾されていないswap
とstd::swap
関連付けられました。
†非公式には、関数呼び出し中に考慮される場合、名前が関連付けられます。詳細については、§3.4.2を参照してください。この場合、std::swap
通常は考慮されません。しかし、それを関連付けて(unqualifiedによって検討されたオーバーロードのセットに追加するswap
)、それを見つけられるようにすることができます。