web-dev-qa-db-ja.com

テンプレートの特殊化と関数のオーバーロード

特殊なテンプレートまたは関数のオーバーロードを使用して、swap(x,y)などの標準ライブラリ関数の独自の実装を提供できるというメモがあります。これは、たとえばSTL containersのように、割り当てスワップ以外のメリットを享受できるすべての型に役立ちます(すでにスワップが書き​​込まれていると思います)。

私の質問は次のとおりです:

  1. 何が良いですか:特別なスワップ実装を提供するテンプレートの特殊化、またはテンプレートなしで使用したい正確なパラメーターを提供する関数のオーバーロード?

  2. なぜそれが良いのですか?または、それらが等しい場合、これはなぜですか?

短編:可能な場合はオーバーロードし、必要な場合は専門化してください。

長い話: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*)を選択します。ただし、2番目のケースでは_int*_特殊化はfoo<int*>(int*)ではなくfoo(T)の特殊化であるため、最初のケースでのみfoo(T*)が見つかります。


あなたは_std::swap_について言及しました。これにより、事態はさらに複雑になります。

この規格では、std名前空間に特殊化を追加できると規定されています。すばらしいので、Fooタイプがいくつかあり、それはパフォーマンスの高いスワップを持っているので、std名前空間でswap(Foo&, Foo&)を特殊化するだけです。問題はありません。

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

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

  1. 独自の名前空間にswap(Foo<T>&, Foo<T>&)関数を作成し、ADLを介してそれが見つかることを期待してください。標準ライブラリがstd::swap(a, b);のようにswapを呼び出すと、ADLが機能しなくなるため、「希望」と言います。

  2. オーバーロードを追加しないという標準の部分を無視して、とにかくそれを実行してください。正直なところ、技術的には許可されていませんが、現実的なシナリオではすべて機能します。

ただし、標準ライブラリがswapを使用するという保証はありません。ほとんどのアルゴリズムは_std::iter_swap_を使用しており、私が見てきたいくつかの実装では、常に_std::swap_に転送されるわけではありません。

68
Peter Alexander

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; }
13
Paolo M

std名前空間の関数をオーバーロードすることは許可されていませんが、テンプレート(私が覚えているように)を特化することが許可されているため、これは1つのオプションです。

もう1つのオプションは、swap関数を操作対象のものと同じ名前空間に配置し、using std::swap;非修飾スワップを呼び出す前。

5
Mark B