web-dev-qa-db-ja.com

関数テンプレートのオーバーロード

誰かが関数テンプレートのオーバーロードのアイデアを要約できますか?重要なのは、テンプレートパラメータまたは関数パラメータですか?戻り値はどうですか?

たとえば、関数テンプレートが与えられた場合

template<typename X, typename Y> void func(X x, Y y) {}

オーバーロードされた関数テンプレートとは何ですか?

1) template<typename X> void func(X x, int y) {}
2) template<typename X, typename Y> X func(X x, Y y) {}
3) template<class X, class Y, class Z> void func(X x, Y y, Z z) {}
26
skydoor

そのリストのうち、2番目のみが曖昧さをもたらします。これは、関数が-テンプレートであるかどうかに関係なく-戻り値の型に基づいてオーバーロードできないためです。

他の2つを使用できます。

_template<typename X> void func(X x, int y);
_

呼び出しの2番目の引数がintの場合に使用されます(例:func("string", 10);

_template<class X, class Y, class Z> void func(X x, Y y, Z z);
_

funcを3つの引数で呼び出す場合に使用されます。


他のいくつかの回答がテンプレート関数と関数のオーバーロードが混ざっていないと言及している理由がわかりません。それらは確かにあり、呼び出す関数を選択する方法には特別なルールがあります。

14.5.5

関数テンプレートは、他の関数テンプレートおよび通常の(非テンプレート)関数でオーバーロードできます。通常の関数は、生成される可能性のある関数テンプレートの特殊化と同じ名前とタイプであっても、関数テンプレートとは関係ありません(つまり、特殊化とは見なされません)。

非テンプレート(または「テンプレートが少ない」)オーバーロードは、テンプレートよりも優先されます。

_template <class T> void foo(T);
void foo(int);

foo(10); //calls void foo(int)
foo(10u); //calls void foo(T) with T = unsigned
_

1つの非テンプレートパラメータを使用した最初のオーバーロードもこのルールに該当します。

複数のテンプレートから選択する場合、より専門的な一致が優先されます。

_template <class T> void foo(T);
template <class T> void foo(T*);

int i;
int* p;
int arr[10];

foo(i);  //calls first
foo(p);   //calls second
foo(arr); //calls second: array decays to pointer
_

標準の同じ章にすべてのルールのより正式な説明があります(関数テンプレート


最後に、2つ以上のオーバーロードがあいまいになる状況がいくつかあります。

_template <class T> void foo(T, int);
template <class T> void foo(int, T);

foo(1, 2);
_

ここでは、両方の候補者が等しく専門化されているため、呼び出しはあいまいです。

(たとえば)_boost::disable_if_を使用して、このような状況を明確にすることができます。たとえば、T = intの場合、2番目のオーバーロードをオーバーロード候補として含めないように指定できます。

_#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
template <class T>
void foo(T x, int i);

template <class T>
typename boost::disable_if<boost::is_same<int, T> >::type
foo(int i, T x);

foo(1, 2); //calls the first
_

ここでライブラリは、T = intの場合、2番目のオーバーロードの戻り値の型に「置換エラー」を生成し、オーバーロード候補のセットから削除します。

実際には、そのような状況に遭遇することはめったにありません。

34
visitor

ここには2つの異なるものがあります:関数テンプレート化と関数オーバーロードです。 2つの異なるテンプレート宣言は相互のオーバーロードになる可能性が高いため、前述のように質問はあまり意味がありません。 (指定する3つの「オーバーロード」は、最初のテンプレートに基づいて構築されるのではなく、同じ関数名に4つのオーバーロードが存在します。)実際の問題は、いくつかのオーバーロードがある場合および呼び出しを呼び出す方法です。望ましい過負荷?

まず、テンプレートが含まれているかどうかに関係なく、戻り値の型はオーバーロードプロセスに参加しません。したがって、#2は#1とうまく機能しません。

第2に、関数テンプレートのオーバーロード解決のルールは、より一般的に使用されるクラステンプレートの特殊化ルールとは異なります。どちらも基本的に同じ問題を解決しますが、

  • クラステンプレートのルールはより単純で強力であり、たとえば再帰を許可し、(メンバー)関数は戻り値の型のみが異なる
  • 関数テンプレートの規則により、コンパイラは関数の引数の型からテンプレートの引数を計算できます

関数テンプレートのオーバーロードを使用して特定の問題を解決できる場合がありますが、ルールが長くなり、複雑さに精通している人が少ないために発生するバグの修正に問題が生じる可能性があります。数年のテンプレートハッキングの後で、微妙な関数テンプレートのオーバーロードさえも可能であることを知りませんでした。 BoostやGCCのSTLなどのライブラリでは、代替アプローチがユビキタスです。テンプレート化されたラッパークラスを使用します。

template< typename X, typename Y >
struct functor {
    void operator()( X x, Y y );
};
template< typename X > // partial specialization: 
struct functor< X, int > { // alternative to overloading for classes
    void operator()( X x, int y );
};

次に、暗黙のインスタンス化構文(山括弧なし)を犠牲にします。それを取り戻したい場合は、別の機能が必要です

template< typename X, typename Y > void func( X x, Y y ) {
    return functor< X, Y >()( x, y );
}

関数のオーバーロードがクラス[部分]特殊化ではできない(控除以外の)何かを実行できるかどうかを知りたいのですが…

そしてもちろん、他のオーバーロードとは引数の数が異なるため、オーバーロード#3があいまいになることはありません。

3
Potatoswatter

私は修正されたままです-以下のコメントを参照してください。元の投稿は変更しません。返信のコンテキストが削除されるためです。コメントを寄せてくれたこと、そして投票しないように親切にしてくれたコメント投稿者に感謝します


テンプレートをマクロプリプロセッサのようにすることを検討してください。マクロプリプロセッサは、コンパイラが参照する前に#definesを展開します。

コンパイラーはテンプレートパラメーターを「拡張」してから、関数宣言を調べます。したがって、テンプレートパラメーター==関数パラメーター。同じ関数を2回宣言すると、エラーが発生します。

戻り値の型について尋ねます。これは関数の「シグネチャ」の一部です。パラメータが同じで戻り値の型が異なる2つの関数は、2つの異なる関数です。

コメントに加えて、Herb Suttersの記事 Why Not Specialize Function Templates のトピックに関する詳細情報。それもお役に立てば幸いです。

1