名前空間
変種
操作

関数テンプレート

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
クラステンプレート
関数テンプレート
その他
 
 
 
 

関数テンプレートは関数のファミリーを定義します。

目次

[編集] 構文

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つの作成されたパラメータが追加される (下記の「簡略化された関数テンプレート」を参照)。

exportはオプションの修飾子で、テンプレートをエクスポートとして宣言した (クラステンプレートで使用した場合、そのすべてのメンバーもエクスポートとして宣言された)。エクスポートされたテンプレートをインスタンス化するファイルは、その定義を含める必要はなかった:宣言で十分だった。exportの実装はまれであり、詳細については互いに意見が一致しなかった。

(C++11まで)

関数テンプレートの短縮構文

プレースホルダ型 (auto または Concept auto) が関数宣言または関数テンプレート宣言のパラメータリストに現れる場合、その宣言は関数テンプレートを宣言し、各プレースホルダに対応する1つの発明されたテンプレートパラメータがテンプレートパラメータリストに追加される。

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以降)
1) 明示的インスタンス化定義 (すべての非デフォルトテンプレートパラメータが明示的に指定されている場合、テンプレート引数推定は行われない)
2) すべてのパラメータに対するテンプレート引数推定を伴う明示的インスタンス化定義
3) 明示的インスタンス化宣言 (すべての非デフォルトテンプレートパラメータが明示的に指定されている場合、テンプレート引数推定は行われない)
4) すべてのパラメータに対するテンプレート引数推定を伴う明示的インスタンス化宣言

明示的なインスタンス化定義は、それが参照する関数またはメンバ関数のインスタンス化を強制する。テンプレート定義の後であれば、プログラムのどこにでも記述でき、特定の引数リストに対して、プログラム中で一度だけ現れることが許可され、診断は不要である。

明示的なインスタンス化宣言 (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)
}

このメカニズムにより、テンプレート演算子を使用することが可能になる。なぜなら、演算子を関数呼び出し式として書き直す以外の方法でテンプレート引数を指定する構文がないからである。

#include <iostream>
 
int main() 
{
    std::cout << "Hello, world" << std::endl;
    // operator<< is looked up via ADL as std::operator<<,
    // then deduced to operator<<<char, std::char_traits<char>> both times
    // std::endl is deduced to &std::endl<char, std::char_traits<char>>
}

テンプレート引数推定は、関数テンプレートの名前探索 (これには引数依存探索が含まれる可能性がある) の後、およびオーバーロード解決の前に実行される。

詳細については、テンプレート引数推定を参照。

[編集] 明示的テンプレート引数

関数テンプレートのテンプレート引数は、以下から取得できる。

  • テンプレート引数推定
  • デフォルトテンプレート引数
  • 明示的に指定されたもので、以下のコンテキストで行うことができる。
  • 関数呼び出し式内
  • 関数のアドレスが取得されたとき
  • 関数への参照が初期化されたとき
  • メンバ関数へのポインタが形成されたとき
  • 明示的特殊化内
  • 明示的インスタンス化内
  • フレンド宣言内

オーバーロードされた演算子変換関数、およびコンストラクタには、関数名を使用せずに呼び出されるため、テンプレート引数を明示的に指定する方法はない。

指定されたテンプレート引数は、種類においてテンプレートパラメータと一致しなければならない (すなわち、型には型、非型には非型、テンプレートにはテンプレート)。パラメータの数よりも引数の数が多いことはできない (ただし、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以降)
  • 戻り値の型とパラメータリスト内のテンプレートパラメータを含む式が同等であること。
  • テンプレートパラメータリストの後ろに続くrequires-clause内の式が存在する場合、それらが同等であること。
  • 関数宣言子の後ろに続くrequires-clause内の式が存在する場合、それらが同等であること。
(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

同じ関数テンプレート特殊化が複数のオーバーロードされた関数テンプレートに一致する場合 (これはテンプレート引数推定の結果としてよく発生する)、最適な一致を選択するためにオーバーロードされた関数テンプレートの部分順序付けが実行される。

具体的には、部分順序付けは以下の状況で発生する。

1) 関数テンプレート特殊化への呼び出しに対するオーバーロード解決
template<class X>
void f(X a);
template<class X>
void f(X* a);
 
int* p;
f(p);
2) 関数テンプレート特殊化のアドレスが取得されたとき
template<class X>
void f(X a);
template<class X>
void f(X* a);
 
void (*p)(int*) = &f;
3) 配置new演算子と一致するように、関数テンプレート特殊化である配置delete演算子が選択されたとき
4) フレンド関数宣言明示的インスタンス化、または明示的特殊化が関数テンプレート特殊化を参照するとき
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以降)と、新しいパラメータ型はcv A&となる。ただし、ref&&である場合、またはrefが存在せず、もう一方のテンプレートの最初のパラメータがrvalue参照型である場合は、型はcv A&&となる(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は次のように調整される。

  • PAの両方が参照型であった場合、どちらがよりcv修飾されているかを判断する (その他の場合は、部分順序付けの目的でcv修飾子は無視される)。
  • Pが参照型の場合、それは参照先の型に置き換えられる。
  • Aが参照型の場合、それは参照先の型に置き換えられる。
  • Pがcv修飾されている場合、Pはそれ自体のcv非修飾バージョンに置き換えられる。
  • Aがcv修飾されている場合、Aはそれ自体のcv非修飾バージョンに置き換えられる。

これらの調整の後、型からのテンプレート引数推定に従って、AからPの推定が行われる。

Pが関数パラメータパックである場合、引数テンプレートの残りの各パラメータ型Aは、関数パラメータパックの宣言子IDの型Pと比較される。各比較は、関数パラメータパックによって展開されるテンプレートパラメータパック内の後続の位置のテンプレート引数を推定する。

Aが関数パラメータパックから変換された場合、それはパラメータテンプレートの残りの各パラメータ型と比較される。

(C++11以降)

変換されたtemplate-1の引数Aがtemplate-2の対応するパラメータPを推定するために使用できるが、その逆はできない場合、このAは、このP/Aペアによって推定される型に関して、Pよりも特殊化されていると見なされる。

両方向で推定が成功し、元のPAが参照型であった場合、追加のテストが行われる。

  • Aが左辺値参照でPが右辺値参照だった場合、APよりも特殊化されていると見なされる。
  • APよりもcv修飾されていた場合、APよりも特殊化されていると見なされる。

その他のすべての場合、このP/Aペアによって推定される型に関して、いずれのテンプレートももう一方よりも特殊化されているとは見なされない。

両方向で各PAを検討した後、検討された各型について、

  • 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以降)

関数テンプレートへの呼び出しをコンパイルするために、コンパイラは非テンプレートオーバーロード、テンプレートオーバーロード、およびテンプレートオーバーロードの特殊化の中から決定する必要がある。

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で説明されているように、関数名を括弧で囲むと引数依存探索が抑制されます。

  • g()参照点 (POR) より前に宣言されたf()の複数のオーバーロード。
#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


  • PORの後に、より良い一致のテンプレートオーバーロードが宣言されている。
#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


  • PORの後に、より良い一致の明示的なテンプレート特殊化が宣言されている。
#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


  • PORの後に、より良い一致のテンプレートオーバーロードが宣言されている。最適な一致の明示的なテンプレート特殊化は、より良い一致のオーバーロードの後に宣言されている。
#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


次に、引数依存探索を使用するケースを考えてみましょう (つまり、より一般的な呼び出し形式f(t)を使用します)。

  • PORの後に、より良い一致のテンプレートオーバーロードが宣言されている。
#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


  • PORの後に、より良い一致のテンプレートオーバーロードが宣言されている。最適な一致の明示的なテンプレート特殊化は、より良い一致のオーバーロードの前に宣言されている。
#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


  • PORの後に、より良い一致のテンプレートオーバーロードが宣言されている。最適な一致の明示的なテンプレート特殊化が最後に宣言されている。
#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


引数がC++の基本型である限り、ADL関連の名前空間は存在しません。したがって、これらのシナリオは上記の非ADLの例と同一です。

オーバーロード解決に関する詳細な規則については、オーバーロード解決を参照してください。

[編集] 関数テンプレートの特殊化

[編集] キーワード

template, extern (C++11以降)

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開された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 部分順序付けにおいて、静的メンバー関数テンプレートのパラメータリストに新しい最初のパラメータが追加された。
静的メンバー関数テンプレートのパラメータリストに新しい最初のパラメータが追加された
追加されない

[編集] 関連項目

English 日本語 中文(简体) 中文(繁體)