関数テンプレート
関数テンプレートは関数のファミリーを定義します。
構文
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++20以上) |
| function-declaration | - | 関数宣言。 宣言された関数の名前はテンプレートの名前になります。 |
| constraint(C++20) | - | この関数テンプレートが受理するテンプレート引数を制限する制約式。 |
| function-declaration-with-placeholders(C++20以上) | - | 少なくともひとつの引数の型がプレースホルダ auto または Concept auto を用いる関数宣言。 それぞれのプレースホルダにつきひとつの自動生成された引数がテンプレート引数リストに追加されます (下の省略形の関数テンプレートを参照してください)。
|
|
|
(C++11未満) |
省略形の関数テンプレート関数宣言または関数テンプレート宣言の引数リスト内にプレースホルダ型 ( void f1(auto); // template<class T> void f(T); と同じです。
void f2(C1 auto); // template<C1 T> void f2(T); と同じです (C1 がコンセプトの場合)。
void f3(C2 auto...); // template<C2... Ts> void f3(Ts...); と同じです (C2 がコンセプトの場合)。
void f4(const C3 auto*, C4 auto&); // template<C3 T, C4 U> void f4(const T*, U&); と同じです。
template <class T, C U>
void g(T x, U y, C auto z); // template<class T, C U, C W> void g(T x, U y, W z); と同じです。
省略形の関数テンプレートはすべての関数テンプレートと同様に特殊化できます。 template<>
void f4<int>(const int*, const double&); // f4<int, const double> の特殊化。
|
(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 テンプレート) は暗黙の実体化を妨げます。 暗黙の実体化を発生させていたであろうコードは、プログラム内のどこか他の場所で提供される明示的実体化の定義を使用しなければなりません。 |
(C++11以上) |
関数テンプレートの特殊化またはメンバ関数テンプレートの特殊化の明示的実体化において、関数の引数から推定できる場合、テンプレート引数は指定しなくても構いません。
template<typename T>
void f(T s)
{
std::cout << s << '\n';
}
template void f<double>(double); // f<double>(double) 実体化します。
template void f<>(char); // f<char>(char) を実体化します (テンプレート引数は推定されます)。
template void f(int); // f<int>(int) を実体化します (テンプレート引数は推定されます)。
関数テンプレートの明示的実体化またはクラステンプレートのメンバ関数の明示的実体化は inline または constexpr を使用することはできません。 明示的実体化の宣言が暗黙に宣言される特別なメンバ関数を表す場合、プログラムは ill-formed です。
constructor の明示的実体化はテンプレート引数リスト (構文 (1)) を使用することはできません。 それらは推定できる (構文 (2)) ため、必要になることもありません。
明示的実体化の宣言は inline 関数、 auto 宣言、参照、およびクラステンプレートの特殊化の暗黙の実体化を抑制しません (そのため、明示的実体化の宣言の対象であるインライン関数が ODR 使用されたとき、それはインライン化のために暗黙に実体化されますが、その非インラインなコピーはその翻訳単位内に生成されません)。
デフォルト引数を持つ関数テンプレートの明示的実体化の定義は、その引数を使用せず、その初期化を試みません。
char* p = 0;
template<class T> T g(T x = &p) { return x; }
template int g<int>(int); // &p は int ではありませんが、 OK です。
暗黙の実体化
関数の定義が存在することを要求する文脈でコードが関数を参照したとき、その特定の関数がまだ明示的に実体化されていなければ、暗黙の実体化が発生します。 文脈から推定できれば、テンプレート引数のリストは供給しなくても構いません。
#include <iostream>
template<typename T>
void f(T s)
{
std::cout << s << '\n';
}
int main()
{
f<double>(1); // f<double>(double) を実体化して呼びます。
f<>('a'); // f<char>(char) を実体化して呼びます。
f(7); // f<int>(int) を実体化して呼びます。
void (*ptr)(std::string) = f; // f<string>(string) を実体化します。
}
ノート: <> を完全に省略するとオーバーロード解決はテンプレートと非テンプレート両方のオーバーロードを探索します。
テンプレートの実引数推定
関数テンプレートを実体化するためには、すべてのテンプレート引数が既知でなければなりませんが、すべてのテンプレート引数が指定されなければならないわけではありません。 可能なときは、コンパイラは関数の引数から欠けているテンプレート引数を推定します。 これは、関数の呼び出しが試みられたとき、および関数テンプレートのアドレスが取得されたときに発生します。
template<typename To, typename From> To convert(From f);
void g(double d)
{
int i = convert<int>(d); // convert<int, double>(double) を実体化して呼びます。
char c = convert<char>(d); // convert<char, double>(double) を実体化して呼びます。
int(*ptr)(float) = convert; // convert<int, float>(float) を実体化します。
}
この仕組みはテンプレート演算子の使用を可能とします。 演算子の場合、それを関数呼び出し式に書き直す以外には、テンプレート引数を指定する構文がないためです。
#include <iostream>
int main()
{
std::cout << "Hello, world" << std::endl;
// operator<< が ADL を通して std::operator<< として探索され、
// その後、 operator<<<char, std::char_traits<char>> と推定されます (両方とも)。
// std::endl は &std::endl<char, std::char_traits<char>> と推定されます。
}
テンプレートの実引数推定は、関数テンプレートの名前探索 (これは実引数依存の名前探索に影響を与える場合があります) の後、オーバーロード解決の前に、行われます。
詳細についてはテンプレートの実引数推定を参照してください。
明示的なテンプレート引数
関数テンプレートのテンプレート引数は以下から取得されます。
- テンプレートの実引数推定。
- デフォルトテンプレート引数。
- 明示的な指定。 これは以下の文脈で行うことができます。
- 関数呼び出し式において。
- 関数のアドレスが取られるとき。
- 関数への参照が初期化されるとき。
- メンバ関数へのポインタが形成されるとき。
- 明示的特殊化において。
- 明示的実体化において。
- フレンド宣言において。
オーバーロード演算子、変換関数、およびコンストラクタについては、関数名を使用せずに呼ばれるため、テンプレート引数を明示的に指定する方法はありません。
指定されたテンプレート実引数はそのテンプレート仮引数とマッチする種類 (すなわち型に対しては型、非型に対しては非型、テンプレートに対してはテンプレート) でなければなりません。 存在する仮引数より多くの実引数を指定することはできません (ただし仮引数のひとつがパラメータパックの場合は除きます。 その場合はそれぞれの非パック仮引数について実引数が存在しなければなりません)。
指定された非型実引数は、対応する非型テンプレート仮引数の型にマッチするか、それに変換可能か、いずれかでなければなりません。
テンプレートの実引数推定に寄与しないテンプレート仮引数 (対応するテンプレート実引数が明示的に指定された場合など) は、対応する関数仮引数の型への暗黙の変換の対象です (通常のオーバーロード解決の場合と同様です)。
明示的に指定されたテンプレートパラメータパックは、追加の実引数が存在する場合、テンプレートの実引数推定によって、拡張されることがあります。
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0); // Types = {int*, float*, int}
}
テンプレートの実引数置換
すべてのテンプレート引数が指定された、推定された、またはデフォルトテンプレート引数から取得されたとき、関数の仮引数リスト内のテンプレート仮引数のすべての使用は対応するテンプレート実引数で置き換えられます。
関数テンプレートの置換に失敗する (つまり、テンプレート仮引数を推定されたまたは提供されたテンプレート実引数で置き換えることに失敗する) と、その関数テンプレートはオーバーロード集合から削除されます。 これはテンプレートメタプログラミングを用いたオーバーロード集合を操作する様々な方法を可能にします。 詳細については 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);
// 同じ型を持つ2つの異なる関数。
// しかし関数内では、 t は異なる cv 修飾を持ちます。
f<int>(1); // 関数の型は void(int) です。 t は int です。
f<const int>(1); // 関数の型は void(int) です。 t は const int です。
// 同じ型と同じ x を持つ2つの異なる関数。
// (これらの2つの関数へのポインタは等しくなく、
// 関数ローカルな static 変数は異なるアドレスを持ちます。)
g<int>(1); // 関数の型は void(int) です。 x は const int です。
g<const int>(1); // 関数の型は void(int) です。 x は const int です。
// 最上段の cv 修飾のみが取り除かれます。
h<const int>(1, NULL); // 関数の型は void(int, const int*) です。
// z は const int です。 zp は const int* です。
関数テンプレートのオーバーロード
関数テンプレートと非テンプレート関数をオーバーロードしても構いません。
非テンプレート関数は同じ型を持つテンプレートの特殊化とは常に異なります。 異なる関数テンプレートの特殊化は、たとえそれらが同じ型を持っていても、お互い常に異なります。 同じ戻り値の型と同じ引数リストを持つ2つの関数テンプレートは異なり、明示的なテンプレート引数リストを用いて区別することができます。
型または非型テンプレート引数を使用する式が関数の引数リスト内または戻り値の型内に現れるとき、その式はオーバーロードの目的のためにその関数テンプレートのシグネチャの一部に残ります。
template<int I, int J> A<I+J> f(A<I>, A<J>); // オーバーロード #1。
template<int K, int L> A<K+L> f(A<K>, A<L>); // #1 と同じ。
template<int I, int J> A<I-J> f(A<I>, A<J>); // オーバーロード #2。
テンプレート引数に影響を与える2つの式は、それらの式を含む2つの関数定義が ODR のルールの下で同じとなるであろう場合、つまり、2つの式が、名前探索を通して同じエンティティに解決される、同じトークンの並びを含む場合 (ただしテンプレート仮引数は異なる名前でも構いません)) 同等であると言います。 2つのラムダ式は同等となることはありません。 (C++20以上)
template <int I, int J> void f(A<I+J>); // テンプレートオーバーロード #1。
template <int K, int L> void f(A<K+L>); // #1 と同等です。
|
2つの依存式が同等かどうかを判定するとき、名前散策の結果ではなく影響のある依存名のみが考慮されます。 同じテンプレートの複数の宣言で名前探索の結果が異なる場合は、最初のそのような宣言が使用されます。 template <class T> decltype(g(T())) h(); // decltype(g(T())) は依存型です。
int g(int);
template <class T> decltype(g(T())) h() { // h() の再宣言は最初の名前探索を使用します。
return g(T()); // たとえ、ここでの名前探索が g(int) を発見するとしても、
}
int i = h<int>(); // テンプレートの実引数置換は失敗します。
// g(int) は h() の最初のスコープにおいて存在しませんでした。
|
(C++14以上) |
2つの関数テンプレートは、以下の場合に、同等とみなされます。
- 同じスコープで宣言されている。
- 同じ名前を持つ。
- 同等なテンプレート仮引数リストを持つ。 これは、そのリストが同じ長さであり、対応する仮引数の組のそれぞれについて以下のすべてが真である、という意味です。
- 2つの仮引数が同じ種類である (どちらも型である、どちらも非型である、またはどちらもテンプレートである)。
- どちらもパラメータパックである、または、どちらもパラメータパックでない。
- 非型の場合は、その型が同等である。
- テンプレートの場合は、そのテンプレート仮引数が同等である。
|
(C++20以上) |
- 戻り値の型または仮引数リスト内のテンプレート仮引数に影響する式が同等である。
|
(C++20以上) |
テンプレート仮引数に影響を与える2つの式は、それらが同等ではないけれども任意のテンプレート実引数の集合についてその2つの式の評価結果が同じ値である場合、機能的に同等であると言います。
2つの関数テンプレートは、それらがその戻り値の型および仮引数リスト内のテンプレート仮引数に影響を与える1つ以上の式が機能的に同等であることを除いて同等である場合、機能的に同等であるとみなされます。
|
また、2つの関数テンプレートは、それらの制約が異なって指定されているけれども同じテンプレート実引数リストの集合を受理し満たされる場合、機能的に同等であると言いますが、同等ではありません。 |
(C++20以上) |
プログラムが機能的に同等だけれども同等でない関数テンプレートの宣言を含む場合、プログラムは ill-formed です (診断は要求されません)。
// 同等。
template <int I> void f(A<I>, A<I+10>); // オーバーロード #1。
template <int I> void f(A<I>, A<I+10>); // オーバーロード #1 の再宣言。
// 同等でない。
template <int I> void f(A<I>, A<I+10>); // オーバーロード #1。
template <int I> void f(A<I>, A<I+11>); // オーバーロード #2。
// 機能的に同等だけれども同等でない。
// このプログラムは ill-formed です (診断は要求されません)。
template <int I> void f(A<I>, A<I+10>); // オーバーロード #1。
template <int I> void f(A<I>, A<I+1+2+3+4>); // オーバーロード #1 と機能的に同等。
同じ関数テンプレートの特殊化が複数のオーバーロードされた関数テンプレートにマッチするとき (これはテンプレートの実引数推定によってしばしば発生します)、ベストマッチを選択するためにオーバーロードされた関数テンプレートの半順序が行われます。
具体的には、半順序は以下の状況で行われます。
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;
| This section is incomplete Reason: mini-example |
template<class X> void f(X a); // 1つめのテンプレート f。
template<class X> void f(X* a); // 2つめのテンプレート f。
template<> void f<>(int *a) {} // 明示的特殊化。
// テンプレートの実引数推定により2つの候補、
// foo<int*>(int*) と f<int>(int*) が挙げられます。
// 半順序はより特殊化されている方として f<int>(int*) を選択します。
非形式的には、「A が B より特殊化されている」とは「A が B より受理する型が少ない」という意味です。
形式的には、任意の2つの関数テンプレートのいずれかより特殊化されているかを決定するために、半順序の処理はまず2つのテンプレートの一方を以下のように変換します。
- それぞれの型、非型、およびテンプレート引数 (パラメータパックを含みます) について、一意な架空の型、値、またはテンプレートが生成され、そのテンプレートの関数の型内に置換されます。
- 比較している2つの関数テンプレートの一方のみがメンバ関数であり 、その関数テンプレートが何らかのクラス
Aの非静的メンバである場合は、その引数リストに新たな第1引数が挿入されます。 その型は、そのメンバ関数テンプレートが && 修飾されていればcv A&&、そうでなければcv A&です (cv はそのメンバ関数テンプレートの cv 修飾です)。 これは演算子の順序付けを助けます。 演算子はメンバ関数としてと非メンバ関数としての両方が名前探索されます。
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; // int B<A>::operator*(R&) に対するテンプレートの実引数推定は R=A を与えます。
// int operator*(T&, R&)に対するテンプレートの実引数推定は T=B<A>, R=A を与えます。
// 半順序の目的のために、メンバテンプレート B<A>::operator* は
// template<class R> int operator*(B<A>&, R&); に変換されます。
// int operator*( T&, R&) T=B<A>, R=A と
// int operator*(B<A>&, R&) R=A の間の半順序は、
// より特殊化されている方として int operator*(B<A>&, A&) を選択します。
2つのテンプレートの一方が上記のように変換された後、その変換されたテンプレートを実引数テンプレートとして、他方のテンプレートの元のテンプレート型を仮引数テンプレートとして用いて、テンプレートの実引数推定を実行します。 この処理は、2つめのテンプレート (変換後の) を実引数として、1つめのテンプレートの元の型式を仮引数として用いて、再度行われます。
順序を決定するために使用される型は文脈に依ります。
- 関数呼び出しの文脈では、型は、その関数呼び出しが実引数を持つ、その関数の仮引数です (デフォルト関数引数、パラメータパック、および省略記号引数は考慮されません ー 下の例を参照してください)。
- ユーザ定義変換関数の呼び出しの文脈では、その変換関数テンプレートの戻り値の型が使用されます。
- それ以外の文脈では、その関数テンプレートの型が使用されます。
仮引数テンプレートからの上記のリストのそれぞれの型が推定されます。 推定を開始する前に、仮引数テンプレートのそれぞれの仮引数 P および実引数テンプレートの対応する実引数 A が以下のように調節されます。
PとAがどちらも以前に参照型であれば、どちらがより多く cv 修飾されているかを判定します (それ以外のすべての場合において、半順序の目的のためには cv 修飾は無視されます)。Pが参照型の場合は、その参照先の型で置き換えられます。Aが参照型の場合は、その参照先の型で置き換えられます。Pが cv 修飾されている場合は、その cv 修飾されていないバージョンで置き換えられます。Aが cv 修飾されている場合は、その cv 修飾されていないバージョンで置き換えられます。
これらの調節の後、 A からの P の推定が型からのテンプレートの実引数推定に従って行われます。
P が関数パラメータパックの場合、実引数テンプレートの残りの仮引数の型それぞれの型 A がその関数パラメータパックの declarator-id の型 P と比較されます。 それぞれの比較は関数パラメータパックによって展開されたテンプレートパラメータパック内の後続の位置に対してテンプレート実引数を推定します。
A が関数パラメータパックから変換された場合、推定は失敗します (C++14未満) それは仮引数テンプレートの残りの仮引数の型それぞれと比較されます (C++14以上)。
変換されたテンプレートその1の実引数 A がテンプレートその2の対応する仮引数 P を推定するために使用できるけれども逆はできない場合、その P/A の組によって推定される型に関して、 A は P より特殊化されています。
両方向で推定が成功し、元の P と A が参照型であった場合は、追加の判定が行われます。
Aが左辺値参照であってPが右辺値参照であった場合、 A は P より特殊化されているとみなされます。AがPより多く cv 修飾されている場合、 A は P より特殊化されているとみなされます。
それ以外のすべてのケースにおいては、その P/A の組によって推定される型に関して、どちらのテンプレートも他方より特殊化されていません。
すべての P と B を両方向で考慮した後、考慮されたそれぞれの型について、
- すべての型についてテンプレートその1がテンプレートその2と少なくとも同程度に特殊化されている
- いくつかの型についてテンプレートその1がテンプレートその2より特殊化されている
- いずれの型についてもテンプレートその2がテンプレートその1より特殊化されていない、いかなる型についても少なくとも同程度に特殊化されていない
場合、テンプレートその1はテンプレートその2より特殊化されています。 テンプレートの順序を入れ替えた後に上記の条件が真である場合、テンプレートその2はテンプレートその1より特殊化されています。 どちらでもなければ、いずれのテンプレートも他方より特殊化されていません。 引き分けの場合、一方の関数テンプレートが末尾パラメータパックを持ち、他方が持たないならば、省略された仮引数を持つ方が空のパラメータパックを持つ方より特殊化されているとみなされます。
オーバーロードされたテンプレートのすべての組を考慮した後、曖昧でなく他のすべてより特殊化されているものがひとつ存在するならば、そのテンプレート特殊化が選択され、そうでなければ、コンパイルは失敗します。
以下の例では、架空の引数は U1、 U2 で表されています。
template<class T> void f(T); // テンプレート #1
template<class T> void f(T*); // テンプレート #2
template<class T> void f(const T*); // テンプレート #3
void m() {
const int* p;
f(p); // オーバーロード解決により以下がピックアップされます。
// #1: void f(T ) [T = const int *]
// #2: void f(T*) [T = const int]
// #3: void f(const T *) [T = int]
// 半順序:
// 変換された #2 から #1: void(U1*) から void(T): A=U1*, P=T: 推定成功: T=U1*
// 変換された #1 から #2: void(U1) から void(T*): A=U1, P=T*: 推定失敗
// T に関して #2 は #1 より特殊化されています。
// 変換された #3 から #1: void(const U1*) から void(T): A=const U1*, P=T: 成功: T=const U1*
// 変換された #1 から #3: void(U1) から void(const T*): A=U1, P=const T*: 失敗
// T に関して #3 は #1 より特殊化されています。
// 変換された #3 から #2: void(const U1*) から void(T*): A=const U1*, P=T*: 成功: T=const U1
// 変換された #2 から #3: void(U1*) から void(const T*): A=U1*, P=const T*: 失敗
// T に関して #3 は #2 より特殊化されています。
// 結果: #3 が選択されます。
// 別の言い方をすると、 f(const T*) は f(T) や 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); // #1 に対する推定: void f(T, T*) [T = int]
// #2 に対する推定: void f(T, int*) [T = int]
// 半順序:
// #2 から #1: void(U1,int*) から void(T,T*): A1=U1, P1=T: T=U1
// A2=int*, P2=T*: T=int: 失敗
// #1 から #2: void(U1,U2*) から void(T,int*): A1=U1, P1=T: T=U1
// A2=U2*, P2=int*: 失敗
// T に関してどちらもより特殊化されていません。 呼び出しは曖昧です。
}
template<class T> void g(T); // テンプレート #1
template<class T> void g(T&); // テンプレート #2
void m() {
float x;
g(x); // #1 からの推定: void g(T ) [T = float]
// #2 からの推定: void g(T&) [T = float]
// 半順序:
// #2 から #1: void(U1&) から void(T): A=U1 (調節後), P=T: 成功
// #1 から #2: void(U1) から void(T&): A=U1, P=T (調節後): 成功
// T に関してどちらもより特殊化されていません。 呼び出しは曖昧です。
}
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); // #1 からの推定: void h(const T &) [T = A<int>]
// #2 からの推定: void h(A<T> &) [T = int]
// 半順序:
// #2 から #1: void(A<U1>&) から void(const T&): A=A<U1>, P=T: 成功: T=A<U1>
// #1 から #2: void(const U1&) から void(A<T>&): A=const U1, P=A<T>: 失敗
// T に関して #2 は #1 より特殊化されています。
const A<int> z2;
h(z2); // #1 からの推定: void h(const T&) [T = A<int>]
// #2 からの推定: void h(A<T>&) [T = int] (しかし置換は失敗します)
// 選択肢となるオーバーロードはひとつしかなく、半順序は試みられません。 #1 が呼ばれます。
}
呼び出しの文脈では明示的な呼び出し実引数が存在する仮引数のみが考慮されるため、関数パラメータパック、省略記号引数、および明示的な呼び出し実引数のないデフォルト引数付き仮引数は無視されます。
template<class T> void f(T); // #1
template<class T> void f(T*, int=1); // #2
void m(int* ip) {
int* ip;
f(ip); // #2 を呼びます (T* は T より特殊化されています)。
}
template<class T> void g(T); // #1
template<class T> void g(T*, ...); // #2
void m(int* ip) {
g(ip); // #2 を呼びます (T* は 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); // #2 を呼びます。
f<int>(42); // エラー、曖昧です。
}
template<class T > void g(T, T = T()); // #1
template<class T, class... U> void g(T, U ...); // #2
void h() {
g(42); // エラー、曖昧です。
}
template<class T, class... U> void f(T, U...); // #1
template<class T > void f(T); // #2
void h(int i) {
f(&i); // パラメータパックと引数なしの間のタイブレーカーにより、 #2 を呼びます。
// (ノート: 以前は DR692 と 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: #1 を呼びます (T* は T より特殊化されています)。
}
template <class ...T> int f(T*...); // #1
template <class T> int f(const T&); // #2
f((int*)0); // OK: #1 を選択します。
// (両方向で推定が失敗するため、 DR1395 より前では曖昧でした)
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(); // #1 を呼びます。
f(1, 2, 3); // #2 を呼びます。
f(1, 2); // #3 を呼びます。 可変長引数でないテンプレート #3 は
// 可変長引数テンプレート #1 および #2 より特殊化されています。
半順序の処理内のテンプレートの実引数推定の間、半順序に対して考慮されるいずれの型においても実引数が使用されない場合、テンプレート仮引数がその実引数にマッチすることは要求されません。
template <class T> T f(int); // #1
template <class T, class U> T f(U); // #2
void g() {
f<int>(1); // #1 の特殊化は明示的です: T f(int) [T = int]
// #2 の特殊化は推定されます: T f(U) [T = int, U = int]
// 半順序 (引数の型のみが考慮されます):
// #2 から #1: U1(U2) から T(int): 失敗。
// #1 から #2: U1(int) から T(U): 成功: U=int, T は使用されません。
// #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<>()); // #1 を呼びます。
g(Tuple<int, float>()); // #2 を呼びます。
g(Tuple<int, float&>()); // #3 を呼びます。
g(Tuple<int>()); // #3 を呼びます。
| This section is incomplete Reason: 14.8.3[temp.over] |
関数テンプレートの呼び出しをコンパイルするために、コンパイラは非テンプレートオーバーロード、テンプレートオーバーロード、およびテンプレートオーバーロードの特殊化の間で決定する必要があります。
template< class T > void f(T); // #1: テンプレートオーバーロード。
template< class T > void f(T*); // #2: テンプレートオーバーロード。
void f(double); // #3: 非テンプレートオーバーロード。
template<> void f(int); // #4: #1 の特殊化。
f('a'); // #1 を呼びます。
f(new int(1)); // #2 を呼びます。
f(1.0); // #3 を呼びます。
f(1); // #4 を呼びます。
関数のオーバーロード vs 関数の特殊化
非テンプレートとプライマリテンプレートのオーバーロードのみがオーバーロード解決に参加することに注意してください。 特殊化はオーバーロードではなく、考慮されません。 オーバーロード解決がベストマッチなプライマリ関数テンプレートを選択した後にのみ、その特殊化がより良くマッチするかどうかを見るために調べられます。
template< class T > void f(T); // #1: すべての型に対するオーバーロード。
template<> void f(int*); // #2: int へのポインタに対する #1 の特殊化。
template< class T > void f(T*); // #3: すべてのポインタ型に対するオーバーロード。
f(new int(1)); // #2 がパーフェクトマッチであったかもしれませんが、 #3 を呼びます。
翻訳単位内でヘッダファイルを順序付けるとき、このルールを覚えておくことは重要です。 関数のオーバーロードと関数の特殊化の間の相互作用のさらなる例については、以下を展開してください。
|
|
|
Consider first some scenarios where the argument-dependent lookup is not employed. For that, we use the call
Run this code #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);
} // POI of g() and f()
// Both #1 and #2 are added to the candidate list;
// #2 is selected because it is a better match.
出力: #2
Run this code #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);
} // POI 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
Run this code #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);
} // POI 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
Run this code #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);
} // POI 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
Run this code #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);
} // POI 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
Run this code #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);
} // POI 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
Run this code #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);
} // POI 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
|
オーバーロード解決の詳細なルールについてはオーバーロード解決を参照してください。
関数テンプレートの特殊化
| This section is incomplete Reason: 14.8[temp.fct.spec] (note that 14.8.1[temp.arg.explicit] is already in full specialization article: either function specifics go here: lack of partials, interaction with function overloads, or just refer to that |
欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
