名前空間
変種
操作

テンプレート引数の推論

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<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)
                                // and stores its address in ptr
}

このメカニズムにより、テンプレート演算子を使用できるようになります。なぜなら、関数呼び出し式として再記述する以外の方法で演算子のテンプレート引数を指定する構文がないためです。

#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>>
}

テンプレート引数推論は、関数テンプレートの名前探索引数依存探索を含む場合がある)の後、テンプレート引数置換SFINAEを含む場合がある)およびオーバーロード解決の前に実行されます。

テンプレート引数推論は、構築されるオブジェクトの型としてクラステンプレートの名前が使用される場合にも実行されます。

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を生成できるテンプレート引数(型テンプレートパラメーターTiの型、テンプレートテンプレートパラメーターTTiのテンプレート、非型テンプレートパラメーターIiの値)を決定しようとします。

複数のパラメーターがある場合、各P/Aペアは個別に推論され、推論されたテンプレート引数は結合されます。いずれかのP/Aペアで推論が失敗するか曖昧な場合、または異なるペアが異なる推論されたテンプレート引数をもたらす場合、またはテンプレート引数が推論も明示的に指定もされていないまま残る場合、コンパイルは失敗します。

Pから参照とcv修飾子を削除するとstd::initializer_list<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

Pから参照とcv修飾子を削除するとP'[N]になり、Aが空でない波括弧初期化子リストである場合、上記と同様に推論が実行されます。ただし、Nが非型テンプレートパラメーターである場合、初期化子リストの長さから推論されます。

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

パラメーターパックが最後のPとして現れる場合、型Pは呼び出しの残りの各引数の型Aと照合されます。各照合は、パック展開の次の位置のテンプレート引数を推論します。

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が関数テンプレートを含まないオーバーロードされた関数セットである場合、各オーバーロードでテンプレート引数推論が試行されます。成功したものが1つだけの場合、その成功した推論が使用されます。成功したものが0個または複数個の場合、テンプレートパラメーターは非推論コンテキストになります(以下を参照)。

template<class T>
int f(T(*p)(T));
 
int g(int);
int g(char);
 
f(g); // P = T(*)(T), A = overload set
      // P = T(*)(T), A1 = int(int): deduced T = int
      // P = T(*)(T), A2 = int(char): fails to deduce T
      // only one overload works, deduction succeeds

推論が始まる前に、PAに対して以下の調整が行われます。

1) Pが参照型でない場合、
a) Aが配列型の場合、Aは配列からポインターへの変換によって取得されたポインター型に置き換えられます。
b) それ以外の場合、Aが関数型の場合、Aは関数からポインターへの変換によって取得されたポインター型に置き換えられます。
c) それ以外の場合、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
2) Pがcv修飾型の場合、推論のために最上位のcv修飾子は無視されます。
3) Pが参照型の場合、参照される型が推論に使用されます。
4) Pがcv非修飾テンプレートパラメーターへの右辺値参照(いわゆる転送参照)であり、対応する関数呼び出し引数が左辺値である場合、推論のためにAの代わりにAへの左辺値参照型が使用されます(注:これはstd::forwardの動作の基礎です。注:クラステンプレート引数推論では、クラステンプレートのテンプレートパラメーターは転送参照にはなりません。(C++17以降))。
template<class T>
int f(T&&);       // P is an rvalue reference to cv-unqualified T (forwarding reference)
 
template<class T>
int g(const T&&); // P is an rvalue reference to cv-qualified T (not special)
 
int main()
{
    int i;
    int n1 = f(i); // argument is lvalue: calls f<int&>(int&) (special case)
    int n2 = f(0); // argument is not lvalue: calls f<int>(int&&)
 
//  int n3 = g(i); // error: deduces to g<int>(const int&&), which
                   // cannot bind an rvalue reference to an lvalue
}

これらの変換の後、以下のセクションで説明されている推論プロセス(cf. 型からの推論)が実行され、推論されたA(つまり、上記でリストされた調整と推論されたテンプレートパラメーターの代入後のP)が、変換されたA(つまり、上記でリストされた調整後のA)と同一になるようなテンプレート引数を見つけようとします。

PAからの通常の推論が失敗した場合、以下の代替案が追加で考慮されます。

1) 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
2) 変換されたAは、cv修飾変換または関数ポインター変換(C++17以降)によって推論されたAに変換できる別のポインター型またはメンバーへのポインター型であっても構いません。
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*)
3) Pがクラスであり、PシンプルテンプレートIDの形式である場合、変換されたAは推論されたAの派生クラスであっても構いません。同様に、Pシンプルテンプレート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を構成するために使用される型、テンプレート、非型値はテンプレート引数推論には参加せず、代わりに、別の場所で推論された、または明示的に指定されたテンプレート引数を使用します。テンプレートパラメーターが非推論コンテキストでのみ使用され、明示的に指定されていない場合、テンプレート引数推論は失敗します。

1) 修飾IDを使用して指定された型のnested-name-specifier(スコープ解決演算子::の左側のすべて)。
// 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以降)
4) 非型テンプレート引数または、サブ式がテンプレートパラメーターを参照する配列境界。
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
5) デフォルト引数を持つ関数パラメーターのパラメーター型で使用されるテンプレートパラメーターで、引数推論が行われている呼び出しで使用されているもの。
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)
6) パラメーターPで、そのAが関数またはオーバーロードのセットであり、Pに一致する関数が複数あるか、またはPに一致する関数がないか、またはオーバーロードのセットに1つ以上の関数テンプレートが含まれる場合。
template<typename T>
void out(const T& value) { std::cout << value; }
 
out("123");     // P = const T&, A = const char[4] lvalue: deduced T = char[4]
out(std::endl); // P = const T&, A = function template: T is in non-deduced context
7) パラメーターPで、そのAが波括弧初期化子リストであるが、Pstd::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 is in non-deduced context
                   // error: T is not explicitly specified or deduced from another P/A
 
g2({1, 2, 3}, 10); // P1 = std::vector<T>, A1 = {1, 2, 3}: T is in non-deduced context
                   // P2 = T, A2 = int: deduced 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以降)
10) 配列型(ただし配列への参照または配列へのポインターではない)の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
}

いずれの場合も、型名の一部が非推論の場合、型名全体が非推論コンテキストになります。ただし、複合型は推論される型名と非推論の型名の両方を含むことができます。例えば、A<T>::B<T2>では、Tは規則 #1(ネストされた名前指定子)により非推論であり、T2は同じ型名の一部であるため非推論ですが、void(*f)(typename A<T>::B, A<T>)では、A<T>::BTは非推論(同じ規則のため)であるのに対し、A<T>Tは推論されます。

[編集] 型からの推論

1つ以上の型テンプレートパラメーターTi、テンプレートテンプレートパラメーターTTi、または非型テンプレートパラメーターIiに依存する関数パラメーターPと、対応する引数Aが与えられた場合、Pが以下のいずれかの形式であると推論が行われます。

  • cv(optional) T;
  • T*;
  • T&;
  • T&&;
(C++11以降)
  • T(optional) [I(optional)];
  • T(optional) (U(optional));
(C++17まで)
  • T(optional) (U(optional)) noexcept(I(optional));
(C++17以降)
  • T(optional) U(optional)::*;
  • TT(optional)<T>;
  • TT(optional)<I>;
  • TT(optional)<TU>;
  • TT(optional)<>.

上記の形式では、

  • T(optional)またはU(optional)は、これらの規則を再帰的に満たす型またはparameter-type-listPまたはAの非推論コンテキスト、またはPAで同じ非依存型を表します。
  • TT(optional)またはTU(optional)は、クラステンプレートまたはテンプレートテンプレートパラメーターを表します。
  • I(optional)は、Iである式、PまたはAで値依存の式、またはPAで同じ定数値を持つ式を表します。
  • noexcept(I(optional))は、おそらく暗黙的なnoexcept指定子のオペランドが上記のI(optional)の規則を満たす例外指定を表します。
(C++17以降)

Pがテンプレートパラメーターリスト<T>または<I>を含む形式のいずれかである場合、そのテンプレート引数リストの各要素Piは、そのAの対応するテンプレート引数Aiと照合されます。最後のPiがパック展開である場合、そのパターンはAのテンプレート引数リストの残りの各引数と比較されます。他に推論されない末尾のパラメーターパックは、空のパラメーターパックに推論されます。

Pが関数パラメーターリスト(T)を含む形式のいずれかである場合、そのリストの各パラメーターPiは、Aの関数パラメーターリストの対応する引数Aiと比較されます。最後のPiがパック展開である場合、その宣言子はAのパラメーター型リストの残りの各Aiと比較されます。

形式はネストされ、再帰的に処理できます。

  • X<int>(*)(char[6])T*の例で、TX<int>(char[6])です。
  • X<int>(char[6])T(optional) (U(optional))の例で、TX<int>Uchar[6]です。
(C++17まで)
  • X<int>(char[6])T(optional) (U(optional)) noexcept(I(optional))の例で、TX<int>Uchar[6]、暗黙的なnoexcept指定子のIfalseです。
(C++17以降)
  • X<int>TT(optional)<T>の例で、TTXTintです。
  • char[6]T(optional) [I(optional)]の例で、TcharIstd::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 can be deduced to equal 20
      // but T cannot be deduced from the type of 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 was deduced to long
                      // from the template argument value in the type A<2>
 
template<auto X>
class bar {};
 
template<class T, T n>
void f(bar<n> x);
 
f(bar<3>{}); // OK: T was deduced to int (and n to 3)
             // from the template argument value in the type bar<3>

T[N]Nの型はstd::size_tです。

template<class T, T i>
void f(int (&a)[i]);
 
int v[10];
f(v); // OK: T is std::size_t

関数型のnoexcept(B)指定子におけるBの型はboolです。

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 is deduced as true and the type of X is deduced as bool.
(C++17以降)

関数テンプレートの非型テンプレートパラメーターが、関数パラメーター(これもテンプレートである)のテンプレートパラメーターリストで使用され、対応するテンプレート引数が推論される場合、推論されたテンプレート引数の型(その囲むテンプレートパラメーターリストで指定されているとおり、つまり参照は保持される)は、cv修飾子が削除される場合を除き、非型テンプレートパラメーターの型と厳密に一致する必要があります。ただし、テンプレート引数が配列境界から推論される場合は、任意の整数型が許可されます。たとえboolであっても常にtrueになります。

template<int i>
class A {};
 
template<short s>
void f(A<s>); // the type of the non-type template param is short
 
void k1()
{
    A<1> a;  // the type of the non-type template param of a is int
 
    f(a);    // P = A<(short)s>, A = A<(int)1>
             // error: deduced non-type template argument does not have the same
             // type as its corresponding template argument
 
    f<1>(a); // OK: the template argument is not deduced,
             // this calls 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>
           // parameter type is int&
           // argument type is int& in struct X's template declaration
           // OK (with CWG 2091): deduces R to refer to n
}

型テンプレートパラメーターは、関数のデフォルト引数の型からは推論できません。

template<typename T>
void f(T = 5, T = 7);
 
void g()
{
    f(1);     // OK: calls f<int>(1, 7)
    f();      // error: cannot deduce T
    f<int>(); // OK: calls f<int>(5, 7)
}

テンプレートテンプレートパラメーターの推論は、関数呼び出しで使用されるテンプレート特殊化で使用される型を使用できます。

template<template<typename> class X>
struct A {}; // A is a template with a TT param
 
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>: deduced TT = B, calls f(A<B>)

[編集] その他のコンテキスト

関数呼び出しや演算子式以外にも、テンプレート引数推論は以下の状況で使用されます。

auto 型推論

テンプレート引数推論は、変数の初期化子からauto 指定子の意味を推論するときに、変数の宣言で使用されます。

パラメーターPは次のように取得されます。宣言された変数の型Tに含まれるautoのすべての出現は、仮想の型テンプレートパラメーターUに置き換えられます。または、初期化がコピーリスト初期化の場合、std::initializer_list<U>に置き換えられます。引数Aは初期化子式です。上記で説明した規則に従ってPAからUが推論された後、推論されたUPに代入されて実際の変数型が取得されます。

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の意味を推論する場合、波括弧初期化子リストは1つの要素のみを含まなければならず、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-returning 関数

テンプレート引数推論は、関数の宣言において、return文から関数の戻り値型におけるauto指定子の意味を推論するときに使用されます。

auto-returning関数では、パラメーターPは次のように取得されます。関数にautoを含む宣言された戻り値型Tのすべてのautoの出現は、仮想の型テンプレートパラメーターUに置き換えられます。引数Areturn文の式であり、return文にオペランドがない場合、Avoid()です。上記で説明した規則に従ってPAからUが推論された後、推論されたUTに代入されて実際の戻り値型が取得されます。

auto f() { return 42; } // P = auto, A = 42:
                        // deduced U = int, the return type of f is int

そのような関数が複数のreturn文を持つ場合、各return文に対して推論が実行されます。結果として得られるすべての型は同じでなければならず、それが実際の戻り値型となります。

そのような関数にreturn文がない場合、推論時にAvoid()です。

注:変数および関数宣言におけるdecltype(auto)プレースホルダーの意味は、テンプレート引数推論を使用しません。

(C++14以降)

[編集] オーバーロード解決

テンプレート引数推論は、候補テンプレート関数から特殊化を生成する際に、オーバーロード解決中に使用されます。PAは通常の関数呼び出しの場合と同じです。

std::string s;
std::getline(std::cin, s);
 
// "std::getline" names 4 function templates,
// 2 of which are candidate functions (correct number of parameters)
 
// 1st candidate template:
// P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin
// P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s
// deduction determines the type template parameters CharT, Traits, and Allocator
// specialization std::getline<char, std::char_traits<char>, std::allocator<char>>
 
// 2nd candidate template:
// P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin
// P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s
// deduction determines the type template parameters CharT, Traits, and Allocator
// specialization std::getline<char, std::char_traits<char>, std::allocator<char>>
 
// overload resolution ranks reference binding from lvalue std::cin
// and picks the first of the two candidate specializations

推論が失敗した場合、または推論は成功したが生成された特殊化が無効である場合(例えば、パラメータがクラス型でも列挙型でもないオーバーロードされた演算子)、その特殊化はオーバーロードセットに含まれません。SFINAEと同様です。

[編集] オーバーロードセットのアドレス

テンプレート引数推論は、関数テンプレートを含むオーバーロードセットのアドレスを取得する際に使用されます。

関数テンプレートの関数型はPです。ターゲット型Aの型です。

std::cout << std::endl;
 
// std::endl names a function template
// type of endl P =
// std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&)
// operator<< parameter A =
// std::basic_ostream<char, std::char_traits<char>>& (*)(
//   std::basic_ostream<char, std::char_traits<char>>&
// )
// (other overloads of operator<< are not viable) 
// deduction determines the type template parameters CharT and Traits

この場合、推論に追加の規則が適用されます。関数パラメーターPiとAiを比較するとき、Piがcv非修飾テンプレートパラメーターへの右辺値参照(「転送参照」)であり、対応するAiが左辺値参照である場合、Piはテンプレートパラメーター型に調整されます(T&& は T になります)。

関数テンプレートの戻り値型がプレースホルダー(autoまたはdecltype(auto))である場合、その戻り値型は非推論コンテキストであり、インスタンス化から決定されます。

(C++14以降)

[編集] 部分順序付け

テンプレート引数推論は、オーバーロードされた関数テンプレートの部分順序付け中に使用されます。

[編集] 変換関数テンプレート

テンプレート引数推論は、ユーザー定義変換関数テンプレート引数を選択する際に使用されます。

Aは変換の結果として要求される型です。Pは変換関数テンプレートの戻り値型です。Pが参照型の場合、このセクションの残りの部分では、参照される型がPの代わりに使用されます。

Aが参照型でない場合

a) Pが配列型の場合、配列からポインターへの変換によって得られるポインター型がPの代わりに使用されます。
b) Pが関数型の場合、関数からポインターへの変換によって得られる関数ポインター型がPの代わりに使用されます。
c) Pがcv修飾されている場合、最上位のcv修飾子は無視されます。

Aがcv修飾されている場合、最上位のcv修飾子は無視されます。Aが参照型の場合、参照される型が推論に使用されます。

PAからの通常の推論(上記参照)が失敗した場合、以下の代替案が追加で考慮されます。

a) Aが参照型の場合、Aは推論されたAよりもcv修飾されていても構いません。
b) Aがポインター型またはメンバーへのポインター型の場合、推論されたAは、cv修飾変換によってAに変換できる任意のポインターであっても構いません。
struct C
{
    template<class T>
    operator T***();
};
C c;
 
const int* const* const* p1 = c;
 
// P = T***, A = const int* const* const*
// regular function-call deduction for
// template<class T> void f(T*** p) as if called with the argument
// of type const int* const* const* fails
// additional deduction for conversion functions determines T = int
// (deduced A is int***, convertible to const int* const* const*)
c) Aが関数ポインター型の場合、推論されたAは、関数ポインター変換によってAに変換できるnoexcept関数へのポインターであっても構いません。
d) Aがメンバー関数へのポインターの場合、推論されたAは、関数ポインター変換によってAに変換できるnoexceptメンバー関数へのポインターであっても構いません。
(C++17以降)

変換関数テンプレートに関するその他の規則については、メンバーテンプレートを参照してください。

[編集] 明示的インスタンス化

テンプレート引数推論は、明示的インスタンス化明示的特殊化、および宣言子IDが関数テンプレートの特殊化を参照するフレンド宣言(例えば、friend ostream& operator<< <> (...))で使用されます。すべてのテンプレート引数が明示的に指定されていないか、デフォルト化されていない場合、テンプレート引数推論が、どのテンプレートの特殊化が参照されているかを決定するために使用されます。

Pは潜在的な一致として考慮されている関数テンプレートの型であり、Aは宣言からの関数型です。一致がないか、または一致が複数ある場合(部分順序付けの後)、関数宣言は不正な形式です。

template<class X>
void f(X a);        // 1st template f
template<class X>
void f(X* a);       // 2nd template f
template<>
void f<>(int* a) {} // explicit specialization of f
 
// P1 = void(X), A1 = void(int*): deduced X = int*, f<int*>(int*)
// P2 = void(X*), A2 = void(int*): deduced X = int, f<int>(int*)
// f<int*>(int*) and f<int>(int*) are then submitted to partial ordering
// which selects f<int>(int*) as the more specialized template

この場合、推論に追加の規則が適用されます。関数パラメーターPiとAiを比較するとき、Piがcv非修飾テンプレートパラメーターへの右辺値参照(「転送参照」)であり、対応するAiが左辺値参照である場合、Piはテンプレートパラメーター型に調整されます(T&& は T になります)。

[編集] デアロケーション関数テンプレート

テンプレート引数推論は、デアロケーション関数テンプレートの特殊化が、特定の配置形式のoperator newと一致するかどうかを判断する際に使用されます。

Pは潜在的な一致として考慮されている関数テンプレートの型であり、Aは検討中の配置operator 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; // when X() throws, operator delete is looked up
                              // P1 = void(void*, T), A1 = void(void*, bool):
                              // deduced T = bool
                              // P2 = void(void*, T), A2 = void(void*, double):
                              // deduced T = double
                              // overload resolution picks operator delete<bool>
    }
    catch(const std::exception&) {}
 
    try
    {
        X* p1 = new (13.2) X; // same lookup, picks 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: deduced TT = vector
 
template<template<class> class TT>
void f(TT<int>);
f(v); // error: TT cannot be deduced as "Vec" because Vec is an alias template

[編集] 暗黙の変換

型推論は暗黙の変換(上記の型調整を除く)を考慮しません。それは、後で発生するオーバーロード解決の役割です。ただし、テンプレート引数推論に参加するすべてのパラメーターで推論が成功し、推論されていないすべてのテンプレート引数が明示的に指定またはデフォルト化されている場合、残りの関数パラメーターは対応する関数引数と比較されます。明示的に指定されたテンプレート引数を代入する前は非依存型であった、残りの各パラメーターPについて、対応する引数APに暗黙的に変換できない場合、推論は失敗します。

テンプレート引数推論にテンプレートパラメーターが関与しない依存型を持つパラメーター、および明示的に指定されたテンプレート引数の置換によって非依存になったパラメーターは、オーバーロード解決中にチェックされます。

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); // for #1, deduction determines T = struct A, but the remaining argument 1
             // cannot be implicitly converted to its parameter void*: deduction fails
             // instantiation of the return type is not requested
             // for #2, deduction determines T = struct A, and the remaining argument 1
             // can be implicitly converted to its parameter int: deduction succeeds
             // the function call compiles as a call to #2 (deduction failure is 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-指定子の式は非推論コンテキストではなかった そうである
CWG 1391 C++98 推論に関与しない引数の暗黙の変換の影響が
指定されていなかった
上記のように指定された
CWG 1591 C++11 波括弧初期化子リストから配列境界と要素型を推論できない 推論が許可された
CWG 2052 C++98 非クラス
非列挙引数を持つ演算子の推論はハードエラーだった
他のオーバーロードがある場合
ソフトエラー
CWG 2091 C++98 参照非型パラメーターの推論が
引数に対する型不一致のために機能しなかった
型不一致が回避された
N3922 C++11 autoの直接リスト初期化はstd::initializer_listを推論する 複数の要素の場合不正な形式
単一要素の場合は要素型を推論
CWG 2355 C++17 関数型のnoexcept指定子の値は推論できなかった 推論可能になった
English 日本語 中文(简体) 中文(繁體)