模板参数推导

模板参数推导（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&&。
如此，就把实参的值分类情形完美地传递到模板函数内部，据此可再完美转发给拷贝语义或移动语义的实现函数。

参考文献

C++