**模板参数推导**（template argument deduction），是在调用C++的模板函数时，由编译器根据使用上下文来推断所调用的模板函数的模板参数。这一概念也适用于类的模板成员函数。

类模板也存在模板参数推导的情形。例如：

```
template <class T> struct eval;
template <template <class, class...> class TT, class T1, class... Rest>
     struct eval<TT<T1, Rest...>> { };
eval<A<int>> eA; // OK: matches partial specialization of eval
```

概念
--

模板函数在定义时，用template<>声明模板参数（或称模板形参）。调用模板函数时，可以在函数名字后用`< >`显式指出模板参数（这时称作模板实参）。例如：

```
 template<class T> void foo(T v1){};
 foo<double>(3);
```

但是，调用模板函数时，如果不显式指明模板参数，而是根据函数的调用实参去推断模板实参，这就是*模板参数推导*。例如上例可进一步考虑情形：

```
 foo(3.14);
```

编译器会推导出这种函数调用的模板实参为T->double.

推导类型
----

编译器比较函数模板的形参（template parameter）与对应的调用实参（argument used in the function call）的类型，以确定模板参数的类型。形参的类型必须是下述特定情形之一：

```
T
const T
volatile T
T&
T*
T[10]
A<T>
C(*)(T)
T(*)()
T(*)(U)
T C::*
C T::*
T U::*
T (C::*)()
C (T::*)()
D (C::*)(T)
C (T::*)(U)
T (C::*)(U)
T (U::*)()
T (U::*)(V)
E[10][i]
B<i>
TT<T>
TT<i>
TT<C>
```

说明：

* T, U, V表示模板类型参数
* 10表示任意整数常量
* i表示模板非类型参数
* [i]表示数组界（ represents an array bound of a reference or pointer type, or a non-major array bound of a normal array）
* TT表示模板的模板参数（template template argument）
* (T), (U), (V)表示参数列表包含至少一个模板类型参数
* ()表示参数列表不包含模板参数
* <T>表示模板参数列表包含至少一个模板类型参数
* <i>表示模板参数列表包含至少一个模板非类型参数
* <C>表示模板参数列表其模板参数不依赖于模板实参

编译器可以从上述几个类型结构的复合类型推导模板参数。

推导规则
----

对于形如：

```
template<typename T> int foo(ParamType param);
```

其模板参数`T`的类型需要从模板函数的实参与形参依照如下规则推导：

* 首先，如果模板函数的实参表达式是引用，首先去除引用；
* 上一步后，如果剩下的实参表达式有顶层的const且/或volatile限定符，去除掉。

因而，从模板函数的实参表达式，不能自动推导出顶层的CV-qualifiers，也不能自动推导出引用类型，需要显式指定。

例如： 

```
   template<class T> void foo(T arg){ arg=101;} // 函数模板     
   const int i=102;
   foo(i); //函数模板实例化为 void foo<int>(int)，实参的const限定已被脱去
   foo<const int&>(v1);//直接显示指明模板参数类型
   template<class T> void foo(const T& arg);//或者偏特化模板函数
```

如果形参还带上&号，声明为引用类型，则不执行const剥除（const-stripping），例如：

```
   const int i=102;
   const int &j=i;
   template<class T> void foo(T& arg) { arg=101;}// 函数模板 
   foo(j); // 编译错误: 'arg': you cannot assign to a variable that is const	 
```

这是因为如果不抑制const剥除，则得到了一个非常量引用型变量，绑定到const变量，这显然是不可接受的。 

实参表达式为数组，模板参数推导的类型为指针。这是因为数组名在实参表达式中自动隐式转换为首元素地址的右值。例如：

```
   int a[9];
   template<class T> void foo(T arg){}; // 函数模板 
   foo(a);// 函数模板实例化为 void foo<int*>(int*)
```

另外，C++11标准明确规定不能由模板参数推导出对应实参为std::initializer\_list的类型。例如：

```
template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T
```

### 类型完美转发

C++11增加了右值引用这一新的数据类型。如：`template<class T> void foo(T&& arg);`"T&&"并不意味着形参arg的数据类型一定是右值引用。其数据类型既可能是左值引用，也可能是右值引用。依据"引用塌缩规则"，有：

* 如果实参表达式是类型A的左值，则模板参数T的类型为左值引用A&，形参arg的类型为左值引用A&；
* 如果实参表达式是类型A的右值（包括纯右值与临终值），则模板参数T的类型为右值引用A&&，形参arg的类型为y右值引用A&&。

如此，就把实参的值分类情形完美地传递到模板函数内部，据此可再完美转发给拷贝语义或移动语义的实现函数。

参考文献
----

1. 1 2  Template argument deduction (C++ only), in IBM XL C/C++ Reference for Linux
2. ↑  模板的模板参数，例如：Stack<int,std::vector<int> > vStack; // integer stack that uses a vector as internal container
3. ↑  § 14.8.2.5/5 of the C++11 standard explicitly states that this is a non-deduced context for a template argument:
 A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have std::initializer\_list or reference to possibly cv-qualified std::initializer\_list type.
4. ↑   參: