Template argument deduction
関数テンプレートをインスタンス化するには、すべてのテンプレート引数が既知である必要がありますが、すべてのテンプレート引数を指定する必要はありません。可能な場合、コンパイラは関数引数から不足しているテンプレート引数を推論します。これは、関数呼び出しが試行されたとき、関数テンプレートのアドレスが取得されたとき、および一部の その他の状況 で発生します:
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) をインスタンス化し、 // そのアドレスを ptr に格納する }
この仕組みにより、テンプレート演算子を使用することが可能になります。演算子に対してテンプレート引数を指定する構文が存在しないため、関数呼び出し式として書き直す以外に方法がないからです:
テンプレート引数推論は、関数テンプレートの name lookup ( argument-dependent lookup を含む場合がある)の後、かつ template argument substitution ( SFINAE を含む場合がある)および overload resolution の前に行われます。
|
クラステンプレートの名前が構築されるオブジェクトの型として使用される場合にも、テンプレート引数推論が行われます: std::pair p(2, 4.5); std::tuple t(4, 3, 2.5); std::copy_n(vi1, 3, std::back_insert_iterator(vi2)); std::for_each(vi.begin(), vi.end(), Foo([&](int i) {...})); auto lck = std::lock_guard(foo.mtx); std::lock_guard lck2(foo.mtx, ul); クラステンプレートのテンプレート引数推論は、宣言および明示的なキャスト式で行われます。詳細については クラステンプレート引数推論 を参照してください。 |
(C++17以降) |
目次 |
関数呼び出しからの推論
テンプレート引数推論は、各パラメータ
P
に代入されたときに、引数
A
の型(以下の調整を適用後)と同一となる、推論された型
deduced
A
を生成するために、テンプレート引数(型テンプレートパラメータ
T
に対する型、テンプレートテンプレートパラメータ
TT
に対するテンプレート、定数テンプレートパラメータ
I
に対する値)を決定しようと試みます。
複数のパラメータが存在する場合、各
P
/
A
ペアは個別に推論され、推論されたテンプレート引数はその後結合されます。いずれかの
P
/
A
ペアでの推論が失敗するか曖昧である場合、または異なるペアが異なる推論されたテンプレート引数を生成する場合、あるいはテンプレート引数が推論も明示的な指定もされずに残る場合、コンパイルは失敗します。
|
template<class T> void f(std::initializer_list<T>); f({1, 2, 3}); // P = std::initializer_list<T>, A = {1, 2, 3} // P'1 = T, A'1 = 1: deduced T = int // P'2 = T, A'2 = 2: deduced T = int // P'3 = T, A'3 = 3: deduced T = int // OK: deduced T = int f({1, "abc"}); // P = std::initializer_list<T>, A = {1, "abc"} // P'1 = T, A'1 = 1: deduced T = int // P'2 = T, A'2 = "abc": deduced T = const char* // error: deduction fails, T is ambiguous
template<class T, int N> void h(T const(&)[N]); h({1, 2, 3}); // deduced T = int, deduced N = 3 template<class T> void j(T const(&)[3]); j({42}); // deduced T = int, array bound is not a parameter, not considered struct Aggr { int i; int j; }; template<int N> void k(Aggr const(&)[N]); k({1, 2, 3}); // error: deduction fails, no conversion from int to Aggr k({{1}, {2}, {3}}); // OK: deduced N = 3 template<int M, int N> void m(int const(&)[M][N]); m({{1, 2}, {3, 4}}); // deduced M = 2, deduced N = 2 template<class T, int N> void n(T const(&)[N], T); n({{1}, {2}, {3}}, Aggr()); // deduced T = Aggr, deduced N = 3
parameter pack
が最後の
template<class... Types> void f(Types&...); void h(int x, float& y) { const int z = x; f(x, y, z); // P = Types&..., A1 = x: deduced first member of Types... = int // P = Types&..., A2 = y: deduced second member of Types... = float // P = Types&..., A3 = z: deduced third member of Types... = const int // calls f<int, float, const int> }
|
(C++11以降) |
P
が関数型、関数へのポインタ型、またはメンバ関数へのポインタ型であり、かつ
A
が関数テンプレートを含まない
オーバーロードされた関数の集合
である場合、各オーバーロードに対してテンプレート引数推論が試みられます。一つだけが成功した場合、その成功した推論が使用されます。一つも成功しないか、複数が成功した場合、テンプレートパラメータは非推論コンテキストとなります(下記参照):
template<class T> int f(T(*p)(T)); int g(int); int g(char); f(g); // P = T(*)(T), A = オーバーロード集合 // P = T(*)(T), A1 = int(int): T = int と推論される // P = T(*)(T), A2 = int(char): T の推論に失敗 // 1つのオーバーロードのみが機能するため、推論は成功する
推論が開始される前に、以下の調整が
P
と
A
に対して行われます:
P
が参照型でない場合、
A
が配列型の場合、
A
は配列からポインタへの変換によって得られるポインタ型で置き換えられる;
A
が関数型である場合、
A
は関数からポインタへの変換によって得られるポインタ型で置き換えられる;
A
がcv修飾型である場合、トップレベルのcv修飾子は推論のために無視されます:
template<class T> void f(T); int a[3]; f(a); // P = T, A = int[3], adjusted to int*: deduced T = int* void b(int); f(b); // P = T, A = void(int), adjusted to void(*)(int): deduced T = void(*)(int) const int c = 13; f(c); // P = T, A = const int, adjusted to int: deduced T = int
P
がcv修飾型である場合、トップレベルのcv修飾子は推論において無視されます。
P
が参照型の場合、参照先の型が推論に使用されます。
P
がcv修飾されていないテンプレートパラメータへの右辺値参照(いわゆる
転送参照
)であり、対応する関数呼び出し引数が左辺値である場合、
A
の代わりに
A
への左辺値参照型が推論に使用される(注:これは
std::forward
の動作の基礎である
。注:
クラステンプレート引数推論
では、クラステンプレートのテンプレートパラメータは転送参照にはならない
(C++17以降)
):
template<class T> int f(T&&); // Pはcv修飾されていないTへの右辺値参照(転送参照) template<class T> int g(const T&&); // Pはcv修飾されたTへの右辺値参照(特別ではない) int main() { int i; int n1 = f(i); // 引数は左辺値: f<int&>(int&)を呼び出す(特別なケース) int n2 = f(0); // 引数は左辺値ではない: f<int>(int&&)を呼び出す // int n3 = g(i); // エラー: g<int>(const int&&)に推論され、 // 左辺値に右辺値参照を束縛できない }
これらの変換の後、以下のように推論プロセスが進行し(cf. セクション
型からの推論
)、推論された
A
(すなわち、上記の調整と推論されたテンプレートパラメータの代入後の
P
)が
変換された
A
、すなわち上記の調整後の
A
と同一となるようなテンプレート引数を見つけようと試みます。
通常の
P
と
A
からの控除が失敗した場合、以下の代替案が追加で考慮されます:
P
が参照型の場合、推論された
A
(すなわち、参照によって参照される型)は、変換された
A
よりも多くのcv修飾を持つことができます:
template<typename T> void f(const T& t); bool a = false; f(a); // P = const T&, adjusted to const T, A = bool: // deduced T = bool, deduced A = const bool // deduced A is more cv-qualified than A
A
は、推論された
A
に
修飾変換
または関数ポインタ変換
(C++17以降)
によって変換可能な別のポインタまたはメンバポインタ型となります:
template<typename T> void f(const T*); int* p; f(p); // P = const T*, A = int*: // deduced T = int, deduced A = const int* // qualification conversion applies (from int* to const int*)
P
がクラスであり、かつ
P
が
simple-template-id
の形式を持つ場合、変換された
A
は推定された
A
の派生クラスとなり得る。同様に、
P
が
simple-template-id
の形式を持つクラスへのポインタである場合、変換された
A
は推定された
A
によって指される派生クラスへのポインタとなり得る:
template<class T> struct B {}; template<class T> struct D : public B<T> {}; template<class T> void f(B<T>&) {} void f() { D<int> d; f(d); // P = B<T>&, adjusted to P = B<T> (a simple-template-id), A = D<int>: // deduced T = int, deduced A = B<int> // A is derived from deduced A }
非推論コンテキスト
以下の場合、
P
を構成するために使用される型、テンプレート、および定数は、テンプレート引数推論に参加せず、代わりに他の場所で推論されたか明示的に指定されたテンプレート引数を
使用
します。テンプレートパラメータが非推論コンテキストでのみ使用され、明示的に指定されていない場合、テンプレート引数推論は失敗します。
// the identity template, often used to exclude specific arguments from deduction // (available as std::type_identity as of C++20) template<typename T> struct identity { typedef T type; }; template<typename T> void bad(std::vector<T> x, T value = 1); template<typename T> void good(std::vector<T> x, typename identity<T>::type value = 1); std::vector<std::complex<double>> x; bad(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>> // P1/A1: deduced T = std::complex<double> // P2 = T, A2 = double // P2/A2: deduced T = double // error: deduction fails, T is ambiguous good(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>> // P1/A1: deduced T = std::complex<double> // P2 = identity<T>::type, A2 = double // P2/A2: uses T deduced by P1/A1 because T is to the left of :: in P2 // OK: T = std::complex<double>
|
2)
パックインデックス指定子
または
パックインデックス式
:
template<typename... Ts> void f(Ts...[0], std::tuple<Ts...>); f(3, std::tuple(5, 'A')); // P2 = std::tuple<Ts...>, A2 = std::tuple<int, char> // P2/A2: deduced first member of Ts... = int // P2/A2: deduced second member of Ts... = char // P1 = Ts...[0], A1 = int: Ts...[0] is in non-deduced context |
(C++26以降) |
|
3)
decltype指定子の式:
template<typename T> void f(decltype(*std::declval<T>()) arg); int n; f<int*>(n); // P = decltype(*declval<T>()), A = int: T is in non-deduced context |
(C++11以降) |
template<std::size_t N> void f(std::array<int, 2 * N> a); std::array<int, 10> a; f(a); // P = std::array<int, 2 * N>, A = std::array<int, 10>: // 2 * N is non-deduced context, N cannot be deduced // note: f(std::array<int, N> a) would be able to deduce N
template<typename T, typename F> void f(const std::vector<T>& v, const F& comp = std::less<T>()); std::vector<std::string> v(3); f(v); // P1 = const std::vector<T>&, A1 = std::vector<std::string> lvalue // P1/A1 deduced T = std::string // P2 = const F&, A2 = std::less<std::string> rvalue // P2 is non-deduced context for F (template parameter) used in the // parameter type (const F&) of the function parameter comp, // that has a default argument that is being used in the call f(v)
P
、その
A
は関数またはオーバーロードの集合であり、複数の関数が
P
に一致するか、関数が
P
に一致しないか、オーバーロード集合に1つ以上の関数テンプレートが含まれる場合:
P
(その
A
が波括弧初期化リストであるが、
P
が
std::initializer_list
またはその(CV修飾可能性のある)参照、あるいは配列への参照ではない場合):
template<class T> void g1(std::vector<T>); template<class T> void g2(std::vector<T>, T x); g1({1, 2, 3}); // P = std::vector<T>, A = {1, 2, 3}: Tは非推定コンテキスト内 // エラー: Tが明示的に指定されていない、または他のP/Aから推定されない g2({1, 2, 3}, 10); // P1 = std::vector<T>, A1 = {1, 2, 3}: Tは非推定コンテキスト内 // P2 = T, A2 = int: T = int と推定
|
8)
パラメータリストの末尾にないパラメータパック
P
:
template<class... Ts, class T> void f1(T n, Ts... args); template<class... Ts, class T> void f2(Ts... args, T n); f1(1, 2, 3, 4); // P1 = T, A1 = 1: deduced T = int // P2 = Ts..., A2 = 2, A3 = 3, A4 = 4: deduced Ts = [int, int, int] f2(1, 2, 3, 4); // P1 = Ts...: Ts is non-deduced context
9)
パラメータ
P
内に現れるテンプレートパラメータリストで、テンプレートパラメータリストの最後にないパック展開を含む場合:
template<int...> struct T {}; template<int... Ts1, int N, int... Ts2> void good(const T<N, Ts1...>& arg1, const T<N, Ts2...>&); template<int... Ts1, int N, int... Ts2> void bad(const T<Ts1..., N>& arg1, const T<Ts2..., N>&); T<1, 2> t1; T<1, -1, 0> t2; good(t1, t2); // P1 = const T<N, Ts1...>&, A1 = T<1, 2>: // deduced N = 1, deduced Ts1 = [2] // P2 = const T<N, Ts2...>&, A2 = T<1, -1, 0>: // deduced N = 1, deduced Ts2 = [-1, 0] bad(t1, t2); // P1 = const T<Ts1..., N>&, A1 = T<1, 2>: // <Ts1..., N> is non-deduced context // P2 = const T<Ts2..., N>&, A2 = T<1, -1, 0>: // <Ts2..., N> is non-deduced context |
(C++11以降) |
P
について、主要な配列の境界:
template<int i> void f1(int a[10][i]); template<int i> void f2(int a[i][20]); // P = int[i][20], array type template<int i> void f3(int (&a)[i][20]); // P = int(&)[i][20], reference to array void g() { int a[10][20]; f1(a); // OK: deduced i = 20 f1<20>(a); // OK f2(a); // error: i is non-deduced context f2<10>(a); // OK f3(a); // OK: deduced i = 10 f3<10>(a); // OK }
いずれにせよ、型名の一部が非推定(non-deduced)である場合、型名全体が非推定コンテキストとなります。ただし、複合型には推定される型名と非推定の型名の両方を含むことができます。例えば、
A
<
T
>
::
B
<
T2
>
において、
T
はルール #1 (ネストされた名前指定子) により非推定であり、
T2
は同じ型名の一部であるため非推定となります。しかし、
void
(
*
f
)
(
typename
A
<
T
>
::
B
, A
<
T
>
)
においては、
A
<
T
>
::
B
内の
T
は(同じルールにより)非推定である一方、
A
<
T
>
内の
T
は推定されます。
型からの推論
関数パラメータ
P
が1つ以上の型テンプレートパラメータ
T
i、テンプレートテンプレートパラメータ
TT
i、または定数テンプレートパラメータ
I
iに依存しており、対応する引数
A
がある場合、
P
が以下の形式のいずれかを持つときには推論が行われます:
|
このセクションは不完全です
理由:マイクロ例を含むテーブルの可能性あり |
-
cv(オプション)T; -
T*; -
T&;
|
(C++11以降) |
-
T(オプション)[I(オプション)];
|
(C++17まで) |
|
(C++17以降) |
-
T(オプション)U(オプション)::*; -
TT(オプション)<T>; -
TT(オプション)<I>; -
TT(オプション)<TU>; -
TT(オプション)<>.
上記の形式において、
-
T(optional) またはU(optional) は、これらの規則を再帰的に満たす型、PまたはAにおける非推定コンテキスト、 もしくはPとAで同一の非依存型を表します。 -
TT(optional) またはTU(optional) は、クラステンプレートまたはテンプレートテンプレートパラメータを表します。 -
I(optional) は、I自体であるか、PまたはAにおいて値依存であるか、PとAで同一の定数値を持つ式を表します。
|
(C++17以降) |
P
がテンプレートパラメータリスト
<T>
または
<I>
を含む形式のいずれかを持つ場合、そのテンプレート引数リストの各要素
P
i は、対応するテンプレート引数
A
i と照合されます。最後の
P
i がパック展開である場合、そのパターンは
A
のテンプレート引数リスト内の残りの各引数と比較されます。それ以外の方法で推論されない末尾のパラメータパックは、空のパラメータパックとして推論されます。
P
が関数パラメータリスト
(T)
を含む形式のいずれかを持つ場合、そのリストの各パラメータ
P
iは、
A
の関数パラメータリストの対応する引数
A
iと比較されます。最後の
P
iがパック展開の場合、その宣言子は
A
のパラメータ型リストに残る各
A
iと比較されます。
フォームはネストして再帰的に処理できます:
-
X
<
int
>
(
*
)
(
char
[
6
]
)
は
T*の一例であり、ここでTは X < int > ( char [ 6 ] ) である;
|
(C++17まで) |
|
(C++17以降) |
-
X
<
int
>
は
TT(オプション)<T>の例であり、ここでTTはX、Tは int です。 -
char
[
6
]
は
T(オプション)[I(オプション)]の例であり、ここでTは char 、Iは std:: size_t ( 6 ) です。
|
型テンプレート引数は、定数テンプレート引数の型から推論できません: template<typename T, T i> void f(double a[10][i]); double v[10][20]; f(v); // P = double[10][i], A = double[10][20]: // i は 20 と推論できる // しかし T は i の型から推論できない |
(C++17 まで) |
|
依存型で宣言された定数テンプレートパラメータ P に対応する引数の値が式から推論されるとき、P の型内のテンプレートパラメータは値の型から推論されます。 template<long n> struct A {}; template<class T> struct C; template<class T, T n> struct C<A<n>> { using Q = T; }; typedef long R; typedef C<A<2>>::Q R; // OK: T は long と推論された // 型 A<2> のテンプレート引数値から template<auto X> class bar {}; template<class T, T n> void f(bar<n> x); f(bar<3>{}); // OK: T は int と推論された (n は 3) // 型 bar<3> のテンプレート引数値から
型
template<class T, T i> void f(int (&a)[i]); int v[10]; f(v); // OK: T は std::size_t
noexcept
(
B
)
関数型指定子における
template<bool> struct A {}; template<auto> struct B; template<auto X, void (*F)() noexcept(X)> struct B<F> { A<X> ax; }; void f_nothrow() noexcept; B<f_nothrow> bn; // OK: X は true と推論され、X の型は bool と推論される |
(C++17 以降) |
関数テンプレートの定数テンプレートパラメータが関数パラメータ(これもテンプレートである)のテンプレートパラメータリストで使用され、対応するテンプレート引数が推論される場合、推論されたテンプレート引数の型(それを囲むテンプレートパラメータリストで指定されている通り、参照は保持される)は、CV修飾子が除去されることを除き、定数テンプレートパラメータの型と正確に一致しなければなりません。ただし、テンプレート引数が配列の境界から推論される場合を除きます。その場合、たとえそれが常に true になるとしても、あらゆる整数型が許可されます:
template<int i> class A {}; template<short s> void f(A<s>); // 定数テンプレートパラメータの型はshort void k1() { A<1> a; // aの定数テンプレートパラメータの型はint f(a); // P = A<(short)s>, A = A<(int)1> // エラー: 推論された定数テンプレート引数は、対応するテンプレート引数と同じ型を持たない f<1>(a); // OK: テンプレート引数は推論されない // これはf<(short)1>(A<(short)1>)を呼び出す } template<int&> struct X; template<int& R> void k2(X<R>&); int n; void g(X<n> &x) { k2(x); // P = X<R>, A = X<n> // パラメータ型はint& // 引数型は構造体Xのテンプレート宣言内でint& // OK (CWG 2091適用): Rがnを参照するように推論される }
型テンプレートパラメータは、関数のデフォルト引数の型から推論することはできません:
template<typename T> void f(T = 5, T = 7); void g() { f(1); // OK: f<int>(1, 7) を呼び出す f(); // エラー: T を推論できない f<int>(); // OK: f<int>(5, 7) を呼び出す }
テンプレートテンプレートパラメータの推論は、関数呼び出しで使用されるテンプレート特殊化で使用される型を利用できます:
template<template<typename> class X> struct A {}; // AはTTパラメータを持つテンプレート template<template<typename> class TT> void f(A<TT>) {} template<class T> struct B {}; A<B> ab; f(ab); // P = A<TT>, A = A<B>: TT = Bと推論され、f(A<B>)を呼び出す
その他のコンテキスト
関数呼び出しと演算子式に加えて、テンプレート引数推論は以下の状況で使用されます:
auto型推論テンプレート引数推論は、変数の宣言において、変数の初期化子から auto指定子 の意味を推論する際に使用されます。
パラメータ
const auto& x = 1 + 2; // P = const U&, A = 1 + 2: // same rules as for calling f(1 + 2) where f is // template<class U> void f(const U& u) // deduced U = int, the type of x is const int& auto l = {13}; // P = std::initializer_list<U>, A = {13}: // deduced U = int, the type of l is std::initializer_list<int> 直接リスト初期化(コピーリスト初期化ではない)において、波括弧初期化リストから auto の意味を推論する場合、波括弧初期化リストは単一の要素のみを含む必要があり、autoの型はその要素の型になります: auto x1 = {3}; // x1 is std::initializer_list<int> auto x2{1, 2}; // error: not a single element auto x3{3}; // x3 is int // (before N3922 x2 and x3 were both std::initializer_list<int>) |
(C++11以降) |
auto戻り値型関数テンプレート引数推論は、関数の宣言において、return文から関数の戻り値型の auto 指定子の意味を推論する際に使用されます。
auto戻り値型関数の場合、パラメータ
auto f() { return 42; } // P = auto, A = 42: // deduced U = int, the return type of f is int このような関数に複数のreturn文がある場合、各return文に対して推論が実行されます。結果の型はすべて同一でなければならず、それが実際の戻り値型となります。
このような関数にreturn文がない場合、推論時には
注記:変数および関数宣言における decltype ( auto ) プレースホルダの意味は、テンプレート引数推論を使用しません。 |
(C++14以降) |
オーバーロード解決
テンプレート引数推論は、
オーバーロード解決
の際に、候補テンプレート関数から特殊化を生成するときに使用されます。
P
と
A
は通常の関数呼び出しの場合と同じです:
std::string s; std::getline(std::cin, s); // "std::getline" は4つの関数テンプレートを指し、 // そのうち2つが候補関数(正しいパラメータ数)です // 1番目の候補テンプレート: // P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // 推論により型テンプレートパラメータ CharT, Traits, Allocator を決定 // 特殊化 std::getline<char, std::char_traits<char>, std::allocator<char>> // 2番目の候補テンプレート: // P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // 推論により型テンプレートパラメータ CharT, Traits, Allocator を決定 // 特殊化 std::getline<char, std::char_traits<char>, std::allocator<char>> // オーバーロード解決は左辺値 std::cin からの参照バインディングを評価し、 // 2つの候補特殊化のうち最初のものを選択します
推論が失敗した場合、または推論が成功したが生成される特殊化が無効である場合(例えば、パラメータがクラス型でも列挙型でもないオーバーロードされた演算子)、その特殊化はオーバーロードセットに含まれません。これは SFINAE と同様の動作です。
オーバーロードセットのアドレス
テンプレート引数推論は、 オーバーロード集合のアドレス取得 時に使用されます。これには関数テンプレートも含まれます。
関数テンプレートの関数型は
P
です。
ターゲット型
は
A
の型です:
std::cout << std::endl; // std::endl は関数テンプレートを指す // endlの型 P = // std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&) // operator<< のパラメータ A = // std::basic_ostream<char, std::char_traits<char>>& (*)( // std::basic_ostream<char, std::char_traits<char>>& // ) // (operator<< の他のオーバーロードは適合しない) // 推論により型テンプレートパラメータ CharT と Traits が決定される
この場合、推論に追加のルールが適用されます:関数パラメータ
P
iと
A
iを比較する際に、いずれかの
P
iがcv修飾されていないテンプレートパラメータへの右辺値参照(「転送参照」)であり、対応する
A
iが左辺値参照である場合、
P
iはテンプレートパラメータ型に調整されます(T&&はTになります)。
|
関数テンプレートの戻り値型がプレースホルダー( auto または decltype ( auto ) )の場合、その戻り値型は非推定コンテキストとなり、インスタンス化から決定されます。 |
(C++14以降) |
部分順序付け
テンプレート引数推論は、 オーバーロードされた関数テンプレートの部分順序付け の際に使用されます。
|
このセクションは不完全です
理由: ミニ例 |
変換関数テンプレート
テンプレート引数推論は、 ユーザー定義変換関数 のテンプレート引数を選択する際に使用されます。
A
は変換の結果として要求される型です。
P
は変換関数テンプレートの戻り値型です。
P
が参照型の場合、この節の以降の部分では
P
の代わりに参照先の型が使用されます。
A
が参照型でない場合:
P
である場合、配列からポインタへの変換によって得られるポインタ型が
P
の代わりに使用される;
P
が関数型の場合、関数からポインタへの変換によって得られる関数ポインタ型が
P
の代わりに使用される;
P
がcv修飾されている場合、トップレベルのcv修飾子は無視されます。
A
がcv修飾されている場合、トップレベルのcv修飾子は無視されます。
A
が参照型の場合、参照先の型が推論に使用されます。
通常の
P
と
A
からの控除(前述の通り)が失敗した場合、以下の代替案が追加で検討されます:
A
が参照型の場合、
A
は推定された
A
よりも多くのcv修飾を持つことができる;
A
がポインタ型またはメンバへのポインタ型である場合、推定された
A
は、修飾変換によって
A
に変換可能な任意のポインタであることが許可されます:
struct C { template<class T> operator T***(); }; C c; const int* const* const* p1 = c; // P = T***, A = const int* const* const* // 通常の関数呼び出し推定では // template<class T> void f(T*** p) が const int* const* const* 型の引数で // 呼び出された場合、失敗する // 変換関数の追加推定により T = int と決定される // (推定された A は int***、const int* const* const* に変換可能)
|
c)
A
が関数ポインタ型の場合、推論された
A
は noexcept 関数へのポインタであってもよく、関数ポインタ変換によって
A
に変換可能である;
d)
A
がメンバ関数へのポインタの場合、推論された
A
は noexcept メンバ関数へのポインタであってもよく、関数ポインタ変換によって
A
に変換可能である。
|
(C++17 以降) |
変換関数テンプレートに関するその他の規則については、 member template を参照してください。
明示的インスタンス化
テンプレート引数推論は、 明示的インスタンス化 、 明示的特殊化 、および宣言子IDが関数テンプレートの特殊化を参照する friend宣言 (例えば、 friend ostream & operator << <> ( ... ) )において、すべてのテンプレート引数が明示的に指定されていない、またはデフォルト設定されていない場合、どのテンプレートの特殊化が参照されているかを決定するためにテンプレート引数推論が使用されます。
P
は、一致候補として検討されている関数テンプレートの型であり、
A
は宣言からの関数型です。一致するものが存在しない場合、または複数の一致がある場合(部分的順序付けの後)、関数宣言は不適格となります:
template<class X> void f(X a); // 1番目のテンプレートf template<class X> void f(X* a); // 2番目のテンプレートf template<> void f<>(int* a) {} // fの明示的特殊化 // P1 = void(X), A1 = void(int*): 推論されたX = int*, f<int*>(int*) // P2 = void(X*), A2 = void(int*): 推論されたX = int, f<int>(int*) // f<int*>(int*)とf<int>(int*)は部分順序付けに送られ // より特殊化されたテンプレートとしてf<int>(int*)が選択される
この場合、推論に追加のルールが適用されます:関数パラメータ
P
iと
A
iを比較する際に、いずれかの
P
iがcv修飾されていないテンプレートパラメータへの右辺値参照(「転送参照」)であり、対応する
A
iが左辺値参照である場合、
P
iはテンプレートパラメータ型に調整されます(T&&はTになります)。
デアロケーション関数テンプレート
テンプレート引数推論は、特定の配置形式の
解放関数
テンプレート特殊化が、指定された配置形式の
operator new
と一致するかどうかを判断する際に使用されます。
P
は、潜在的なマッチとして検討されている関数テンプレートの型であり、
A
は、検討中の配置new演算子に対するマッチとなるデアロケーション関数の関数型です。マッチがない場合、または複数のマッチがある場合(オーバーロード解決後)、配置デアロケーション関数は呼び出されません(メモリリークが発生する可能性があります):
struct X { X() { throw std::runtime_error(""); } static void* operator new(std::size_t sz, bool b) { return ::operator new(sz); } static void* operator new(std::size_t sz, double f) { return ::operator new(sz); } template<typename T> static void operator delete(void* ptr, T arg) { ::operator delete(ptr); } }; int main() { try { X* p1 = new (true) X; // X()が例外をスローした場合、operator deleteが検索される // P1 = void(void*, T), A1 = void(void*, bool): // 推論された T = bool // P2 = void(void*, T), A2 = void(void*, double): // 推論された T = double // オーバーロード解決により operator delete<bool> が選択される } catch(const std::exception&) {} try { X* p1 = new (13.2) X; // 同じ検索が行われ、operator delete<double> が選択される } catch(const std::exception&) {} }
エイリアステンプレート
エイリアステンプレート は推論されない 、ただし クラステンプレート引数推論 の場合を除く (C++20以降) :
template<class T> struct Alloc {}; template<class T> using Vec = vector<T, Alloc<T>>; Vec<int> v; template<template<class, class> class TT> void g(TT<int, Alloc<int>>); g(v); // OK: TTはvectorと推論される template<template<class> class TT> void f(TT<int>); f(v); // エラー: Vecはエイリアステンプレートであるため、TTを"Vec"として推論できない
暗黙の型変換
型推論は暗黙の変換(上記の型調整を除く)を考慮しません:それは後で行われる
オーバーロード解決
の仕事です。ただし、テンプレート引数推論に参加するすべてのパラメータで推論が成功し、推論されないすべてのテンプレート引数が明示的に指定されているかデフォルトの場合、残りの関数パラメータは対応する関数引数と比較されます。明示的に指定されたテンプレート引数の置換前に非依存だった型を持つ各残存パラメータ
P
について、対応する引数
A
が
P
に暗黙変換できない場合、推論は失敗します。
テンプレート引数推論にテンプレートパラメータが関与しない依存型を持つパラメータ、および明示的に指定されたテンプレート引数の置換によって非依存となったパラメータは、オーバーロード解決の際にチェックされます:
template<class T> struct Z { typedef typename T::x xx; }; template<class T> typename Z<T>::xx f(void*, T); // #1 template<class T> void f(int, T); // #2 struct A {} a; int main() { f(1, a); // #1の場合、推論によりT = struct Aと決定されるが、残りの引数1は // そのパラメータvoid*に暗黙的に変換できない:推論は失敗 // 戻り値型のインスタンス化は要求されない // #2の場合、推論によりT = struct Aと決定され、残りの引数1は // そのパラメータintに暗黙的に変換できる:推論は成功 // 関数呼び出しは#2への呼び出しとしてコンパイルされる(推論失敗はSFINAE) }
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 70 | C++98 | 配列の境界が推論されるかどうかが規定されていなかった | 非推論として規定 |
| CWG 300 | C++98 |
type(*)(T)/T(*)()/T(*)(T)
形式の関数パラメータに対して推論が行われたが、
関数ポインタはこれらの形式にマッチするが関数参照はマッチしない |
これらの形式を
type(T)/T()/T(T)
に変更し、
参照もカバーできるようにした |
| CWG 322 | C++98 |
参照型の型パラメータが推論のために参照先の型を使用するように
調整されていなかった |
調整を追加 |
| CWG 976 | C++98 |
変換演算子テンプレートの推論において、
const T&
戻り値型が
T
結果型にマッチすることはなかった
|
ルールを調整して
そのようなマッチを許可 |
| CWG 1387 | C++11 | decltype-specifierの式が非推論コンテキストではなかった | 非推論コンテキストである |
| CWG 1391 | C++98 |
推論に関与しない引数の暗黙変換の効果が
規定されていなかった |
上記のように規定 |
| CWG 1591 | C++11 | braced-init-list から配列の境界と要素型を推論できない | 推論を許可 |
| CWG 2052 | C++98 |
非クラス非列挙の引数を持つ演算子の推論は
ハードエラーであった |
他のオーバーロードがある場合は
ソフトエラー |
| CWG 2091 | C++98 |
参照定数パラメータの推論は引数との型不一致により
機能しなかった |
型不一致を回避 |
| N3922 | C++11 | auto の直接リスト初期化は std::initializer_list を推論する |
複数要素の場合は不適格、
単一要素の場合は要素型を推論 |
| CWG 2355 | C++17 | 関数型の noexcept 指定子の値は推論可能ではなかった | 推論可能に変更 |