40

教科書関数オーバーロードのテンプレート特化によるswap(x,y)のような標準ライブラリ関数の独自の実装を提供することができます。これは、代入スワップ以外のもの、例えばSTL containers(これは既にスワップが書かれていますが、私が知っている)のようなものから利益を得ることができるあらゆるタイプに役立ちます。テンプレートの特殊化VS関数のオーバーロード

私の質問は以下のとおりです。

  1. より良い何:あなたの専門 スワップ実装を与えるために、テンプレートの特殊、または関数のオーバーロードを使用すると、テンプレートなしで使用する正確な パラメータを提供しますか?

  2. なぜそれは良いですか?それとも彼らが平等であれば、これはなぜですか?

答えて

60

ショートストーリー:できるだけオーバーロードして、必要なときに特化してください。

ロングストーリー:C++では、特殊化と過負荷の扱いが大きく異なります。これは一例で最もよく説明されています。

template <typename T> void foo(T); 
template <typename T> void foo(T*); // overload of foo(T) 
template <>   void foo<int>(int*); // specialisation of foo(T*) 

foo(new int); // calls foo<int>(int*); 

ここで最後の2文字を入れ替えましょう。

template <typename T> void foo(T); 
template <>   void foo<int*>(int*); // specialisation of foo(T) 
template <typename T> void foo(T*); // overload of foo(T) 

foo(new int); // calls foo(T*) !!! 

コンパイラーは、特殊化されていないものも見ています。したがって、どちらの場合も、オーバーロードの解像度はfoo(T*)を選択します。ただし、int*の特殊化はfoo(T)の特殊化であり、foo(T*)ではないため、最初のケースでのみfoo<int*>(int*)が見つかります。


std::swapと記載しました。これは物事をさらに複雑にします。

標準では、std名前空間に特殊化を追加できます。あなたはFooのタイプを持っています。それにはパフォーマンススワップがあり、次にを指定すると、std名前空間にswap(Foo&, Foo&)があります。問題はありません。

Fooがテンプレートクラスの場合はどうなりますか? C++には関数の部分的な特殊化がないため、swapを特化することはできません。唯一の選択はオーバーロードですが、標準では、std名前空間にオーバーロードを追加することはできません。

あなたはこの時点で2つのオプションがあります。

  1. は、独自の名前空間にswap(Foo<T>&, Foo<T>&)関数を作成し、それがADLを経由していますことを願っています。標準ライブラリがstd::swap(a, b);のようなスワップを呼び出すと、単にADLが機能しなくなるので、私は "希望"と言います。

  2. 標準のオーバーロードを追加しないでくださいと言っている部分を無視してください。正直なところ、技術的には許可されていないにもかかわらず、現実的なシナリオではうまくいくはずです。

ただし、標準ライブラリがswapを使用するという保証はありません。ほとんどのアルゴリズムはstd::iter_swapを使用していますが、私が調べた実装によっては、必ずしもstd::swapに転送されるとは限りません。

+0

グレート例サー – tenfour

+6

この点は指摘された...「ので、私は希望を言います」数年前にWG21に登場しました。すべての標準ライブラリ実装者は、そうでないことを知っています。 – MSalters

+0

素敵な答え、ありがとうございました。 –

5

std名前空間の関数のオーバーロードは許可されていませんが、あなたはテンプレートを特殊化することができます(私が思い出すように)。

もう1つの方法は、swapの機能を、操作しているものと同じ名前空間に入れ、using std::swap;を非修飾スワップを呼び出す前に置くことです。

+0

私は 'using std :: swap;'を 'namespace std;'を使うのが好きです。 –

+0

いくつかのテンプレートを特化することができます:_ "標準ライブラリテンプレートのテンプレート特殊化は、宣言がユーザ定義型に依存し、その特殊化が元のテンプレートの標準ライブラリ要件を満たしていて、明示的に禁止されています "_ [n3337; 17.6.4.2.1/1] 'std :: swap'を' std'以外の型に特化させることができます。 – boycy

+0

なぜstd関数をオーバーロードできないのですか?悪い習慣ですが、std名前空間は特別ではありません。私はそれが好きではありませんが、多くの製品はstd名前空間での型のためのstd swapを追加します。 – Nick

10

Peter Alexanderの答えに追加することはほとんどありません。ファンクション特化がオーバーロードよりも先行する可能性がある用途の1つについて言及しておきます。パラメータなしの中から選択する必要がある場合

など。

template<class T> T zero(); 
template<> int zero() { return 0; } 
template<> long zero() { return 0L; } 

関数のオーバーロードを使用して、類似した何かを、あなたが関数のシグネチャにパラメータを追加する必要があります:

int zero(int) { return 0; } 
long zero(long) { return 0L; } 
+1

興味深い使い方:) –

関連する問題