関数テンプレート
関数テンプレートは関数のファミリーを定義します。
目次 |
[編集] 構文
template < parameter-list > function-declaration |
(1) | ||||||||
template < parameter-list > requires constraint function-declaration |
(2) | (C++20以降) | |||||||
| function-declaration-with-placeholders | (3) | (C++20以降) | |||||||
export template < parameter-list > function-declaration |
(4) | (C++11で削除) | |||||||
[編集] 解説
| parameter-list | - | 空でないコンマ区切りのテンプレートパラメータのリスト。各パラメータは、非型パラメータ、型パラメータ、テンプレートパラメータ、またはそれらのいずれかのパラメータパック(C++11以降)である。他のテンプレートと同様に、パラメータは制約されていてもよい(C++20以降) |
| function-declaration | - | 関数宣言。宣言された関数名はテンプレート名となる。 |
| constraint (制約) | - | この関数テンプレートが受け入れるテンプレートパラメータを制限する制約式 |
| function-declaration- with-placeholders |
- | 少なくとも1つのパラメータの型がプレースホルダautoまたはConcept autoを使用する関数宣言:テンプレートパラメータリストには、各プレースホルダに対して1つの作成されたパラメータが追加される (下記の「簡略化された関数テンプレート」を参照)。 |
|
|
(C++11まで) |
関数テンプレートの短縮構文プレースホルダ型 ( void f1(auto); // same as template<class T> void f1(T) void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept void f3(C2 auto...); // same as template<C2... Ts> void f3(Ts...), if C2 is a concept void f4(const C3 auto*, C4 auto&); // same as template<C3 T, C4 U> void f4(const T*, U&); template<class T, C U> void g(T x, U y, C auto z); // same as template<class T, C U, C W> void g(T x, U y, W z); 簡略化された関数テンプレートは、すべての関数テンプレートと同様に特殊化できる。 template<> void f4<int>(const int*, const double&); // specialization of f4<int, const double>
|
(C++20以降) |
[編集] 関数テンプレートのシグネチャ
すべての関数テンプレートにはシグネチャがある。
template-headのシグネチャは、テンプレートパラメータ名とデフォルト引数を除くテンプレートパラメータリストである。そしてrequires-clause (もしあれば)(C++20以降)。
関数テンプレートのシグネチャには、名前、パラメータ型リスト、戻り値の型、末尾のrequires-clause (もしあれば)(C++20以降)、およびtemplate-headのシグネチャが含まれる。以下のケースを除き、そのシグネチャには囲む名前空間も含まれる。
関数テンプレートがクラスメンバーである場合、そのシグネチャには囲む名前空間の代わりに、関数がメンバーであるクラスが含まれる。そのシグネチャには末尾のrequires-clause (もしあれば)(C++20以降)、ref-qualifier (もしあれば)、および(C++11以降)cv-qualifiers (もしあれば)も含まれる。
|
関数テンプレートが、囲むテンプレートパラメータを含む制約を持つフレンドである場合、そのシグネチャには囲む名前空間の代わりに、囲むクラスが含まれる。 |
(C++20以降) |
[編集] 関数テンプレートのインスタンス化
関数テンプレート自体は型でも関数でもない。テンプレート定義のみを含むソースファイルからはコードは生成されない。何らかのコードが出現するためには、テンプレートをインスタンス化する必要がある。つまり、コンパイラが実際の関数 (またはクラステンプレートからクラス) を生成できるように、テンプレート引数を決定する必要がある。
[編集] 明示的インスタンス化
template return-type name < argument-list > ( parameter-list ) ; |
(1) | ||||||||
template return-type name ( parameter-list ) ; |
(2) | ||||||||
extern template return-type name < argument-list > ( parameter-list ) ; |
(3) | (C++11以降) | |||||||
extern template return-type name ( parameter-list ) ; |
(4) | (C++11以降) | |||||||
明示的なインスタンス化定義は、それが参照する関数またはメンバ関数のインスタンス化を強制する。テンプレート定義の後であれば、プログラムのどこにでも記述でき、特定の引数リストに対して、プログラム中で一度だけ現れることが許可され、診断は不要である。
|
明示的なインスタンス化宣言 (extern template) は、暗黙のインスタンス化を抑制する。暗黙のインスタンス化を引き起こす可能性のあるコードは、プログラムのどこか別の場所で提供される明示的なインスタンス化定義を使用する必要がある。 |
(C++11以降) |
関数テンプレートの特殊化またはメンバー関数テンプレートの特殊化の明示的インスタンス化において、末尾のテンプレート引数は、関数パラメータから推定できる場合、省略できる。
template<typename T> void f(T s) { std::cout << s << '\n'; } template void f<double>(double); // instantiates f<double>(double) template void f<>(char); // instantiates f<char>(char), template argument deduced template void f(int); // instantiates f<int>(int), template argument deduced
関数テンプレートまたはクラステンプレートのメンバー関数の明示的インスタンス化には、inlineまたはconstexprを使用できない。明示的インスタンス化の宣言が暗黙的に宣言された特殊メンバー関数を命名する場合、プログラムは不適格である。
コンストラクタの明示的インスタンス化は、テンプレートパラメータリスト (構文(1)) を使用できない。これは、常に推定可能であるため (構文(2))、必要がない。
|
プロスペクティブデストラクタの明示的インスタンス化は、クラスの選択されたデストラクタを命名しなければならない。 |
(C++20以降) |
明示的インスタンス化宣言は、インライン関数、auto宣言、参照、およびクラステンプレート特殊化の暗黙的インスタンス化を抑制しない。(したがって、明示的インスタンス化宣言の対象であるインライン関数がODR-useされた場合、インライン化のために暗黙的にインスタンス化されるが、その非インラインコピーはこの翻訳単位では生成されない)
デフォルト引数を持つ関数テンプレートの明示的インスタンス化定義は、引数の使用ではなく、それらを初期化しようとしない。
char* p = 0; template<class T> T g(T x = &p) { return x; } template int g<int>(int); // OK even though &p isn’t an int.
[編集] 暗黙的インスタンス化
関数の定義が存在する必要があるコンテキストでコードが関数を参照する場合、または定義の存在がプログラムのセマンティクスに影響を与える場合(C++11以降)、この特定の関数が明示的にインスタンス化されていない場合、暗黙的インスタンス化が発生する。テンプレート引数のリストは、コンテキストから推定できる場合、提供する必要はない。
#include <iostream> template<typename T> void f(T s) { std::cout << s << '\n'; } int main() { f<double>(1); // instantiates and calls f<double>(double) f<>('a'); // instantiates and calls f<char>(char) f(7); // instantiates and calls f<int>(int) void (*pf)(std::string) = f; // instantiates f<string>(string) pf("∇"); // calls f<string>(string) }
|
式の定数評価に必要な場合、たとえその式の定数評価が要求されなくても、あるいは定数式評価が定義を使用しなくても、関数の定義の存在がプログラムのセマンティクスに影響を与えるものとみなされる。 template<typename T> constexpr int f() { return T::value; } template<bool B, typename T> void g(decltype(B ? f<T>() : 0)); template<bool B, typename T> void g(...); template<bool B, typename T> void h(decltype(int{B ? f<T>() : 0})); template<bool B, typename T> void h(...); void x() { g<false, int>(0); // OK: B ? f<T>() : 0 is not potentially constant evaluated h<false, int>(0); // error: instantiates f<int> even though B evaluates to false // and list-initialization of int from int cannot be narrowing } |
(C++11以降) |
注: <>を完全に省略すると、オーバーロード解決でテンプレートと非テンプレートの両方のオーバーロードを調べることができる。
[編集] テンプレート引数推定
関数テンプレートをインスタンス化するには、すべてのテンプレート引数がわかっている必要があるが、すべてのテンプレート引数を指定する必要はない。可能な場合、コンパイラは関数引数から不足しているテンプレート引数を推定する。これは、関数呼び出しが試行されたときと、関数テンプレートのアドレスが取得されたときに発生する。
template<typename To, typename From> To convert(From f); void g(double d) { int i = convert<int>(d); // calls convert<int,double>(double) char c = convert<char>(d); // calls convert<char,double>(double) int(*ptr)(float) = convert; // instantiates convert<int, float>(float) }
このメカニズムにより、テンプレート演算子を使用することが可能になる。なぜなら、演算子を関数呼び出し式として書き直す以外の方法でテンプレート引数を指定する構文がないからである。
テンプレート引数推定は、関数テンプレートの名前探索 (これには引数依存探索が含まれる可能性がある) の後、およびオーバーロード解決の前に実行される。
詳細については、テンプレート引数推定を参照。
[編集] 明示的テンプレート引数
関数テンプレートのテンプレート引数は、以下から取得できる。
- テンプレート引数推定
- デフォルトテンプレート引数
- 明示的に指定されたもので、以下のコンテキストで行うことができる。
- 関数呼び出し式内
- 関数のアドレスが取得されたとき
- 関数への参照が初期化されたとき
- メンバ関数へのポインタが形成されたとき
- 明示的特殊化内
- 明示的インスタンス化内
- フレンド宣言内
オーバーロードされた演算子、変換関数、およびコンストラクタには、関数名を使用せずに呼び出されるため、テンプレート引数を明示的に指定する方法はない。
指定されたテンプレート引数は、種類においてテンプレートパラメータと一致しなければならない (すなわち、型には型、非型には非型、テンプレートにはテンプレート)。パラメータの数よりも引数の数が多いことはできない (ただし、1つのパラメータがパラメータパックである場合は、各非パックパラメータに引数が必要である)(C++11以降)。
指定された非型引数は、対応する非型テンプレートパラメータの型と一致するか、それらに変換可能でなければならない。
テンプレート引数推定に参加しない関数パラメータ (例:対応するテンプレート引数が明示的に指定されている場合) は、対応する関数パラメータの型への暗黙的な変換の対象となる (通常のオーバーロード解決と同様)。
|
明示的に指定されたテンプレートパラメータパックは、追加の引数がある場合、テンプレート引数推定によって拡張されることがある。 template<class... Types> void f(Types... values); void g() { f<int*, float*>(0, 0, 0); // Types = {int*, float*, int} } |
(C++11以降) |
[編集] テンプレート引数置換
すべてのテンプレート引数が指定され、推定され、またはデフォルトテンプレート引数から取得された後、関数パラメータリスト内のテンプレートパラメータの使用はすべて、対応するテンプレート引数に置き換えられる。
関数テンプレートの置換失敗 (すなわち、テンプレートパラメータを推定された、または提供されたテンプレート引数に置き換えることに失敗すること) は、その関数テンプレートをオーバーロードセットから削除する。これにより、テンプレートメタプログラミングを使用してオーバーロードセットを操作する多くの方法が可能になる。詳細については、SFINAEを参照。
置換後、配列型および関数型のすべての関数パラメータはポインタに調整され、すべてのトップレベルのcv修飾子は関数パラメータから削除される (通常の関数宣言と同様)。
トップレベルのcv修飾子の削除は、関数内で現れるパラメータの型には影響しない。
template<class T> void f(T t); template<class X> void g(const X x); template<class Z> void h(Z z, Z* zp); // two different functions with the same type, but // within the function, t has different cv qualifications f<int>(1); // function type is void(int), t is int f<const int>(1); // function type is void(int), t is const int // two different functions with the same type and the same x // (pointers to these two functions are not equal, // and function-local statics would have different addresses) g<int>(1); // function type is void(int), x is const int g<const int>(1); // function type is void(int), x is const int // only top-level cv-qualifiers are dropped: h<const int>(1, NULL); // function type is void(int, const int*) // z is const int, zp is const int*
[編集] 関数テンプレートのオーバーロード
関数テンプレートと非テンプレート関数はオーバーロードできる。
非テンプレート関数は、同じ型を持つテンプレート特殊化とは常に異なる。異なる関数テンプレートの特殊化は、同じ型を持つ場合でも互いに常に異なる。同じ戻り値の型と同じパラメータリストを持つ2つの関数テンプレートは異なり、それらの明示的なテンプレート引数リストによって区別できる。
型または非型テンプレートパラメータを使用する式が関数パラメータリストまたは戻り値の型に現れる場合、その式はオーバーロードの目的のために関数テンプレートシグネチャの一部として残る。
template<int I, int J> A<I+J> f(A<I>, A<J>); // overload #1 template<int K, int L> A<K+L> f(A<K>, A<L>); // same as #1 template<int I, int J> A<I-J> f(A<I>, A<J>); // overload #2
テンプレートパラメータを含む2つの式は、ODRの下でこれらの式を含む2つの関数定義が同じになる場合、つまり、2つの式が同じトークンシーケンスを含み、それらの名前が名前探索を通じて同じエンティティに解決される場合、ただしテンプレートパラメータの名前が異なる可能性がある場合、同等であると呼ばれる。2つのラムダ式は決して同等ではない。(C++20以降)
template<int I, int J> void f(A<I+J>); // template overload #1 template<int K, int L> void f(A<K+L>); // equivalent to #1
2つの依存式が同等かどうかを判断する際、名前探索の結果ではなく、関与する依存名のみが考慮される。同じテンプレートの複数の宣言が名前探索の結果で異なる場合、最初のそのような宣言が使用される。
template<class T> decltype(g(T())) h(); // decltype(g(T())) is a dependent type int g(int); template<class T> decltype(g(T())) h() { // redeclaration of h() uses earlier lookup return g(T()); // although the lookup here does find g(int) } int i = h<int>(); // template argument substitution fails; g(int) // was not in scope at the first declaration of h()
2つの関数テンプレートは、以下の場合に同等と見なされる。
- 同じスコープで宣言されていること
- 同じ名前を持っていること
- 同等のテンプレートパラメータリストを持っていること。これは、リストが同じ長さであり、対応する各パラメータペアについて、以下のすべてが真であることを意味する。
- 2つのパラメータが同じ種類であること (両方とも型、両方とも非型、または両方ともテンプレート)。
|
(C++11以降) |
- 非型の場合、その型が同等であること。
- テンプレートの場合、そのテンプレートパラメータが同等であること。
|
(C++20以降) |
- 戻り値の型とパラメータリスト内のテンプレートパラメータを含む式が同等であること。
|
(C++20以降) |
テンプレートパラメータを含む2つの潜在的に評価される(C++20以降)式は、同等ではないが、任意のテンプレート引数のセットに対して、2つの式の評価が同じ値になる場合、機能的に同等であると呼ばれる。
2つの関数テンプレートは、同等であると見なされるが、戻り値の型とパラメータリスト内のテンプレートパラメータを含む1つ以上の式が機能的に同等である場合を除く。
|
さらに、2つの関数テンプレートは、制約が異なる方法で指定されているが、同じテンプレート引数リストのセットを受け入れて満たす場合、機能的に同等であるが同等ではない。 |
(C++20以降) |
プログラムに、機能的に同等であるが同等ではない関数テンプレートの宣言が含まれる場合、プログラムは不適格である。診断は不要である。
// equivalent template<int I> void f(A<I>, A<I+10>); // overload #1 template<int I> void f(A<I>, A<I+10>); // redeclaration of overload #1 // not equivalent template<int I> void f(A<I>, A<I+10>); // overload #1 template<int I> void f(A<I>, A<I+11>); // overload #2 // functionally-equivalent but not equivalent // This program is ill-formed, no diagnostic required template<int I> void f(A<I>, A<I+10>); // overload #1 template<int I> void f(A<I>, A<I+1+2+3+4>); // functionally equivalent
同じ関数テンプレート特殊化が複数のオーバーロードされた関数テンプレートに一致する場合 (これはテンプレート引数推定の結果としてよく発生する)、最適な一致を選択するためにオーバーロードされた関数テンプレートの部分順序付けが実行される。
具体的には、部分順序付けは以下の状況で発生する。
template<class X> void f(X a); template<class X> void f(X* a); int* p; f(p);
template<class X> void f(X a); template<class X> void f(X* a); void (*p)(int*) = &f;
template<class X> void f(X a); // first template f template<class X> void f(X* a); // second template f template<> void f<>(int *a) {} // explicit specialization // template argument deduction comes up with two candidates: // f<int*>(int*) and f<int>(int*) // partial ordering selects f<int>(int*) as more specialized
非公式には、「AはBよりも特殊化されている」とは「AはBよりも少ない型を受け入れる」という意味である。
形式的には、2つの関数テンプレートのうちどちらがより特殊化されているかを判断するために、部分順序付けプロセスはまず2つのテンプレートのいずれかを次のように変換する。
- 各型、非型、およびテンプレートパラメータについて、パラメータパックを含む、(C++11以降)一意の架空の型、値、またはテンプレートが生成され、テンプレートの関数型に代入される。
- 比較されている2つの関数テンプレートのうち一方がメンバー関数であり、その関数テンプレートがクラス
Aの非静的メンバーである場合、そのパラメータリストに新しい最初のパラメータが挿入される。関数テンプレートのcv修飾子をcvとし、関数テンプレートのref-qualifierをrefとする(C++11以降)と、新しいパラメータ型はcvA&となる。ただし、refが&&である場合、またはrefが存在せず、もう一方のテンプレートの最初のパラメータがrvalue参照型である場合は、型はcvA&&となる(C++11以降)。これは、メンバー関数としても非メンバー関数としても探索される演算子の順序付けに役立つ。
struct A {}; template<class T> struct B { template<class R> int operator*(R&); // #1 }; template<class T, class R> int operator*(T&, R&); // #2 int main() { A a; B<A> b; b * a; // template argument deduction for int B<A>::operator*(R&) gives R=A // for int operator*(T&, R&), T=B<A>, R=A // For the purpose of partial ordering, the member template B<A>::operator* // is transformed into template<class R> int operator*(B<A>&, R&); // partial ordering between // int operator*( T&, R&) T=B<A>, R=A // and int operator*(B<A>&, R&) R=A // selects int operator*(B<A>&, A&) as more specialized }
2つのテンプレートのうち一方が上記のように変換された後、変換されたテンプレートを引数テンプレートとして、もう一方のテンプレートの元のテンプレート型をパラメータテンプレートとして使用して、テンプレート引数推定が実行される。次に、第2のテンプレート (変換後) を引数として、最初のテンプレートを元の形式でパラメータとして使用して、プロセスが繰り返される。
順序を決定するために使用される型はコンテキストに依存する。
- 関数呼び出しのコンテキストでは、型は関数呼び出しが引数を持つ関数パラメータ型である (デフォルト関数引数、パラメータパック、(C++11以降)および省略記号パラメータは考慮されない -- 下記の例を参照)。
- ユーザー定義変換関数への呼び出しのコンテキストでは、変換関数テンプレートの戻り値の型が使用される。
- その他のコンテキストでは、関数テンプレート型が使用される。
パラメータテンプレートのリストの各型が推定される。推定が始まる前に、パラメータテンプレートの各パラメータPと、引数テンプレートの対応する引数Aは次のように調整される。
PとAの両方が参照型であった場合、どちらがよりcv修飾されているかを判断する (その他の場合は、部分順序付けの目的でcv修飾子は無視される)。Pが参照型の場合、それは参照先の型に置き換えられる。Aが参照型の場合、それは参照先の型に置き換えられる。Pがcv修飾されている場合、Pはそれ自体のcv非修飾バージョンに置き換えられる。Aがcv修飾されている場合、Aはそれ自体のcv非修飾バージョンに置き換えられる。
これらの調整の後、型からのテンプレート引数推定に従って、AからPの推定が行われる。
|
|
(C++11以降) |
変換されたtemplate-1の引数Aがtemplate-2の対応するパラメータPを推定するために使用できるが、その逆はできない場合、このAは、このP/Aペアによって推定される型に関して、Pよりも特殊化されていると見なされる。
両方向で推定が成功し、元のPとAが参照型であった場合、追加のテストが行われる。
Aが左辺値参照でPが右辺値参照だった場合、AはPよりも特殊化されていると見なされる。AがPよりもcv修飾されていた場合、AはPよりも特殊化されていると見なされる。
その他のすべての場合、このP/Aペアによって推定される型に関して、いずれのテンプレートももう一方よりも特殊化されているとは見なされない。
両方向で各PとAを検討した後、検討された各型について、
- template-1がすべての型に対してtemplate-2と同じかそれ以上に特殊化されている場合
- template-1がいくつかの型に対してtemplate-2よりも特殊化されている場合
- template-2がどの型に対してもtemplate-1よりも特殊化されていない、またはどの型に対しても同じかそれ以上に特殊化されていない場合
その場合、template-1はtemplate-2よりも特殊化されている。テンプレートの順序を入れ替えた後も上記の条件が真である場合、template-2はtemplate-1よりも特殊化されている。それ以外の場合、いずれのテンプレートももう一方よりも特殊化されているとは見なされない。
|
タイの場合、一方の関数テンプレートが末尾のパラメータパックを持ち、もう一方が持たない場合、省略されたパラメータを持つテンプレートが空のパラメータパックを持つテンプレートよりも特殊化されていると見なされる。 |
(C++11以降) |
オーバーロードされたテンプレートのすべてのペアを考慮した後、他のすべてよりも明確に特殊化されているものが1つあれば、そのテンプレートの特殊化が選択され、そうでなければコンパイルは失敗する。
以下の例では、架空の引数はU1、U2と呼ばれる。
template<class T> void f(T); // template #1 template<class T> void f(T*); // template #2 template<class T> void f(const T*); // template #3 void m() { const int* p; f(p); // overload resolution picks: #1: void f(T ) [T = const int *] // #2: void f(T*) [T = const int] // #3: void f(const T *) [T = int] // partial ordering: // #1 from transformed #2: void(T) from void(U1*): P=T A=U1*: deduction ok: T=U1* // #2 from transformed #1: void(T*) from void(U1): P=T* A=U1: deduction fails // #2 is more specialized than #1 with regards to T // #1 from transformed #3: void(T) from void(const U1*): P=T, A=const U1*: ok // #3 from transformed #1: void(const T*) from void(U1): P=const T*, A=U1: fails // #3 is more specialized than #1 with regards to T // #2 from transformed #3: void(T*) from void(const U1*): P=T* A=const U1*: ok // #3 from transformed #2: void(const T*) from void(U1*): P=const T* A=U1*: fails // #3 is more specialized than #2 with regards to T // result: #3 is selected // in other words, f(const T*) is more specialized than f(T) or f(T*) }
template<class T> void f(T, T*); // #1 template<class T> void f(T, int*); // #2 void m(int* p) { f(0, p); // deduction for #1: void f(T, T*) [T = int] // deduction for #2: void f(T, int*) [T = int] // partial ordering: // #1 from #2: void(T,T*) from void(U1,int*): P1=T, A1=U1: T=U1 // P2=T*, A2=int*: T=int: fails // #2 from #1: void(T,int*) from void(U1,U2*): P1=T A1=U1: T=U1 // P2=int* A2=U2*: fails // neither is more specialized w.r.t T, the call is ambiguous }
template<class T> void g(T); // template #1 template<class T> void g(T&); // template #2 void m() { float x; g(x); // deduction from #1: void g(T ) [T = float] // deduction from #2: void g(T&) [T = float] // partial ordering: // #1 from #2: void(T) from void(U1&): P=T, A=U1 (after adjustment), ok // #2 from #1: void(T&) from void(U1): P=T (after adjustment), A=U1: ok // neither is more specialized w.r.t T, the call is ambiguous }
template<class T> struct A { A(); }; template<class T> void h(const T&); // #1 template<class T> void h(A<T>&); // #2 void m() { A<int> z; h(z); // deduction from #1: void h(const T &) [T = A<int>] // deduction from #2: void h(A<T> &) [T = int] // partial ordering: // #1 from #2: void(const T&) from void(A<U1>&): P=T A=A<U1>: ok T=A<U1> // #2 from #1: void(A<T>&) from void(const U1&): P=A<T> A=const U1: fails // #2 is more specialized than #1 w.r.t T const A<int> z2; h(z2); // deduction from #1: void h(const T&) [T = A<int>] // deduction from #2: void h(A<T>&) [T = int], but substitution fails // only one overload to choose from, partial ordering not tried, #1 is called }
呼び出しコンテキストでは明示的な呼び出し引数があるパラメータのみが考慮されるため、関数パラメータパック、(C++11以降)省略記号パラメータ、および明示的な呼び出し引数がないデフォルト引数を持つパラメータは無視される。
template<class T> void f(T); // #1 template<class T> void f(T*, int = 1); // #2 void m(int* ip) { int* ip; f(ip); // calls #2 (T* is more specialized than T) }
template<class T> void g(T); // #1 template<class T> void g(T*, ...); // #2 void m(int* ip) { g(ip); // calls #2 (T* is more specialized than T) }
template<class T, class U> struct A {}; template<class T, class U> void f(U, A<U, T>* p = 0); // #1 template<class U> void f(U, A<U, U>* p = 0); // #2 void h() { f<int>(42, (A<int, int>*)0); // calls #2 f<int>(42); // error: ambiguous }
template<class T> void g(T, T = T()); // #1 template<class T, class... U> void g(T, U...); // #2 void h() { g(42); // error: ambiguous }
template<class T, class... U> void f(T, U...); // #1 template<class T> void f(T); // #2 void h(int i) { f(&i); // calls #2 due to the tie-breaker between parameter pack and no parameter // (note: was ambiguous between DR692 and DR1395) }
template<class T, class... U> void g(T*, U...); // #1 template<class T> void g(T); // #2 void h(int i) { g(&i); // OK: calls #1 (T* is more specialized than T) }
template<class... T> int f(T*...); // #1 template<class T> int f(const T&); // #2 f((int*)0); // OK: selects #2; non-variadic template is more specialized than // variadic template (was ambiguous before DR1395 because deduction // failed in both directions)
template<class... Args> void f(Args... args); // #1 template<class T1, class... Args> void f(T1 a1, Args... args); // #2 template<class T1, class T2> void f(T1 a1, T2 a2); // #3 f(); // calls #1 f(1, 2, 3); // calls #2 f(1, 2); // calls #3; non-variadic template #3 is more // specialized than the variadic templates #1 and #2
部分順序付けプロセス内のテンプレート引数推定中、引数が部分順序付けのために考慮される型のいずれにも使用されない場合、テンプレートパラメータは引数と一致する必要はない。
template<class T> T f(int); // #1 template<class T, class U> T f(U); // #2 void g() { f<int>(1); // specialization of #1 is explicit: T f(int) [T = int] // specialization of #2 is deduced: T f(U) [T = int, U = int] // partial ordering (only considering the argument type): // #1 from #2: T(int) from U1(U2): fails // #2 from #1: T(U) from U1(int): ok: U=int, T unused // calls #1 }
|
テンプレートパラメータパックを含む関数テンプレートの部分順序付けは、それらのテンプレートパラメータパックに対して推定される引数の数に依存しない。 template<class...> struct Tuple {}; template<class... Types> void g(Tuple<Types...>); // #1 template<class T1, class... Types> void g(Tuple<T1, Types...>); // #2 template<class T1, class... Types> void g(Tuple<T1, Types&...>); // #3 g(Tuple<>()); // calls #1 g(Tuple<int, float>()); // calls #2 g(Tuple<int, float&>()); // calls #3 g(Tuple<int>()); // calls #3 |
(C++11以降) |
| このセクションは未完成です 理由: 14.8.3[temp.over] |
関数テンプレートへの呼び出しをコンパイルするために、コンパイラは非テンプレートオーバーロード、テンプレートオーバーロード、およびテンプレートオーバーロードの特殊化の中から決定する必要がある。
template<class T> void f(T); // #1: template overload template<class T> void f(T*); // #2: template overload void f(double); // #3: non-template overload template<> void f(int); // #4: specialization of #1 f('a'); // calls #1 f(new int(1)); // calls #2 f(1.0); // calls #3 f(1); // calls #4
[編集] 関数のオーバーロード vs 関数の特殊化
オーバーロード解決には、非テンプレートとプライマリテンプレートのオーバーロードのみが参加することに注意してください。特殊化はオーバーロードではなく、考慮されません。オーバーロード解決が最適なプライマリ関数テンプレートを選択した後、その特殊化が、より良い一致があるかどうかを調べるために検査されます。
template<class T> void f(T); // #1: overload for all types template<> void f(int*); // #2: specialization of #1 for pointers to int template<class T> void f(T*); // #3: overload for all pointer types f(new int(1)); // calls #3, even though specialization of #1 would be a perfect match
このルールは、翻訳単位のヘッダーファイルを順序付ける際に覚えておくことが重要です。関数のオーバーロードと関数の特殊化の相互作用のさらなる例については、以下を展開してください。
| 例 |
|---|
|
まず、引数依存探索が使用されないいくつかのシナリオを考えてみましょう。そのために、呼び出し(f)(t)を使用します。ADLで説明されているように、関数名を括弧で囲むと引数依存探索が抑制されます。
このコードを実行 #include <iostream> struct A {}; template<class T> void f(T) { std::cout << "#1\n"; } // overload #1 before f() POR template<class T> void f(T*) { std::cout << "#2\n"; } // overload #2 before f() POR template<class T> void g(T* t) { (f)(t); // f() POR } int main() { A* p = nullptr; g(p); // POR of g() and f() } // Both #1 and #2 are added to the candidate list; // #2 is selected because it is a better match. 出力 #2
このコードを実行 #include <iostream> struct A {}; template<class T> void f(T) { std::cout << "#1\n"; } // #1 template<class T> void g(T* t) { (f)(t); // f() POR } template<class T> void f(T*) { std::cout << "#2\n"; } // #2 int main() { A* p = nullptr; g(p); // POR of g() and f() } // Only #1 is added to the candidate list; #2 is defined after POR; // therefore, it is not considered for overloading even if it is a better match. 出力 #1
このコードを実行 #include <iostream> struct A {}; template<class T> void f(T) { std::cout << "#1\n"; } // #1 template<class T> void g(T* t) { (f)(t); // f() POR } template<> void f<>(A*) { std::cout << "#3\n"; } // #3 int main() { A* p = nullptr; g(p); // POR of g() and f() } // #1 is added to the candidate list; #3 is a better match defined after POR. The // candidate list consists of #1 which is eventually selected. After that, the explicit // specialization #3 of #1 declared after POI is selected because it is a better match. // This behavior is governed by 14.7.3/6 [temp.expl.spec] and has nothing to do with ADL. 出力 #3
このコードを実行 #include <iostream> struct A {}; template<class T> void f(T) { std::cout << "#1\n"; } // #1 template<class T> void g(T* t) { (f)(t); // f() POR } template<class T> void f(T*) { std::cout << "#2\n"; } // #2 template<> void f<>(A*) { std::cout << "#3\n"; } // #3 int main() { A* p = nullptr; g(p); // POR of g() and f() } // #1 is the only member of the candidate list and it is eventually selected. // After that, the explicit specialization #3 is skipped because it actually // specializes #2 declared after POR. 出力 #1
このコードを実行 #include <iostream> struct A {}; template<class T> void f(T) { std::cout << "#1\n"; } // #1 template<class T> void g(T* t) { f(t); // f() POR } template<class T> void f(T*) { std::cout << "#2\n"; } // #2 int main() { A* p = nullptr; g(p); // POR of g() and f() } // #1 is added to the candidate list as a result of the ordinary lookup; // #2 is defined after POR but it is added to the candidate list via ADL lookup. // #2 is selected being the better match. 出力 #2
このコードを実行 #include <iostream> struct A {}; template<class T> void f(T) { std::cout << "#1\n"; } // #1 template<class T> void g(T* t) { f(t); // f() POR } template<> void f<>(A*) { std::cout << "#3\n"; } // #3 template<class T> void f(T*) { std::cout << "#2\n"; } // #2 int main() { A* p = nullptr; g(p); // POR of g() and f() } // #1 is added to the candidate list as a result of the ordinary lookup; // #2 is defined after POR but it is added to the candidate list via ADL lookup. // #2 is selected among the primary templates, being the better match. // Since #3 is declared before #2, it is an explicit specialization of #1. // Hence the final selection is #2. 出力 #2
このコードを実行 #include <iostream> struct A {}; template<class T> void f(T) { std::cout << "#1\n"; } // #1 template<class T> void g(T* t) { f(t); // f() POR } template<class T> void f(T*) { std::cout << "#2\n"; } // #2 template<> void f<>(A*) { std::cout << "#3\n"; } // #3 int main() { A* p = nullptr; g(p); // POR of g() and f() } // #1 is added to the candidate list as a result of the ordinary lookup; // #2 is defined after POR but it is added to the candidate list via ADL lookup. // #2 is selected among the primary templates, being the better match. // Since #3 is declared after #2, it is an explicit specialization of #2; // therefore, selected as the function to call. 出力 #3
|
オーバーロード解決に関する詳細な規則については、オーバーロード解決を参照してください。
[編集] 関数テンプレートの特殊化
| このセクションは未完成です 理由: 14.8[temp.fct.spec] (14.8.1[temp.arg.explicit]はすでに完全特殊化の記事にあることに注意してください: ここに関数固有のものを置くか: 部分特殊化の欠如、関数オーバーロードとの相互作用、または単に参照するか) |
[編集] キーワード
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 214 | C++98 | 部分順序付けの正確な手順が指定されていなかった。 | 仕様が追加されました |
| CWG 532 | C++98 | 非静的メンバー関数テンプレートと非メンバー関数テンプレートの間の順序が指定されていなかった。 と非メンバー関数テンプレートの順序が指定されていなかった |
仕様が追加されました |
| CWG 581 | C++98 | コンストラクタテンプレートの明示的特殊化またはインスタンス化におけるテンプレート引数リストが許可されていた コンストラクタテンプレートのインスタンス化が許可されていた |
禁止 |
| CWG 1321 | C++98 | 最初の宣言と再宣言で同じ依存名が同等であるか不明瞭であった 最初の宣言と再宣言の依存名が同等であるか不明瞭であった |
それらは同等であり、 意味は最初の宣言と同じである。 最初の宣言と同じである |
| CWG 1395 | C++11 | Aがパックから来た場合、推定が失敗し、空のパックのタイブレーカーがなかった。 と、空のパックのタイブレーカーがなかった。 |
推定が許可され、 タイブレーカーが追加された。 |
| CWG 1406 | C++11 | 非静的メンバー関数テンプレートに追加された新しい最初のパラメータの型が、そのテンプレートのref-qualifierに関連していなかった。 非静的メンバー関数テンプレートに追加された テンプレートのref-qualifierに関連していなかった |
ref-qualifierが&&の場合、型は右辺値参照型となる。ref-qualifierが &&の場合、参照型である。 |
| CWG 1446 | C++11 | ref-qualifierのない非静的メンバー関数テンプレートに追加された新しい最初のパラメータの型が、そのメンバー関数テンプレートが最初のパラメータが右辺値参照型である関数テンプレートと比較される場合でも、左辺値参照型であった。 ref-qualifierのない非静的メンバー関数テンプレートに追加された新しい最初のパラメータの型は左辺値参照型であり、 そのメンバー関数テンプレートが、最初のパラメータが右辺値参照型である 関数テンプレートと比較される場合でも、左辺値参照型であった |
この場合、型は右辺値参照型となる。 右辺値参照型となる。 この場合、型は右辺値参照型となる。 |
| CWG 2373 | C++98 | 部分順序付けにおいて、静的メンバー関数テンプレートのパラメータリストに新しい最初のパラメータが追加された。 静的メンバー関数テンプレートのパラメータリストに新しい最初のパラメータが追加された |
追加されない |