模板 (C++)

模板（Template）指C++程序设计语言中的函数模板与类模板，是一种参数化类型机制，大体对应于java和C＃中的泛型，但也有一些功能上的显著差异（C++模板支持后两者没有明确对应的模板模板参数和模板非类型参数，但不支持Java的通配符以及C#的泛型类型约束）。模板是C++的泛型编程中不可缺少的一部分。
模板是C++程序员绝佳的武器，特别是结合了多重继承与运算符重载之后。C++的标准函数库提供的许多有用的函数大多结合了模板的概念，如STL以及iostream。

模板的声明与定义

模板定义以关键字template开始，后接模板形参表（template parameter list），模板形参表是用尖括号括住的一个或者多个模板形参的列表，形参之间以逗号分隔。模板形参可以是表示类型的类型形参（type parameter），也可以是表示常量表达式的非类型形参（non-type parameter）。非类型形参跟在类型说明符之后声明。类型形参跟在关键字class或typename之后声明。模板形参可以给出默认值（default arguments for template parameters）。

模板的模板参数

类模板的模板参数允许是另外一个类模板，这称为模板的模板参数（template template parameter），也译作“模板参数模板”。函数模板不允许有模板的模板参数。例如：
template<template <class T> class X> class A { }; //类模板A的第二个参数是另外一个类模板X
template<class T> class B { };
A<B> a; //模板A的实际使用。其中的B是模板的模板实参（template template argument）

模板参数的默认值

模板形参可以给出默认值（default arguments for template parameters）。如果一个模板参数给出了默认值，那么模板形参列表中在其后声明的模板参数都应该给出默认值。例如：
template<class T = char, class U, class V = int> class X { }; //编译出错，或者给出U的默认值，或者不给出T的默认值
一个模板的各次声明给出的模板参数的默认值可以累积其效果。例如：
template<class T, class U = int> class A;
template<class T = float, class U> class A;
template<class T, class U> class A {
  public:
     T x;
     U y;
};
A<> a; //a.x is float, and the type of a.y is int
但是如果交换本示例第一行与第二行的次序，将编译报错。因为如果第一个模板参数T有了默认值，此时编译器必须已经知道其后的第二个模板参数U的默认值。
在同一个作用域（scope）中，不能对同一个模板的同一个参数多次声明其默认值。例如：
template<class T = char> class X;
template<class T = char> class X { };//编译报错。如果在本行中不给出模板参数T的默认值将编译通过
模板参数的作用域为从其声明之处至该模板的定义结束之处。因此可以使用一个模板参数作为其后声明的其他模板参数的一部分或默认值。例如：
template<class V, V obj> class C; 
template<class T, class U = T> class D { };

变量模板

变量模板（variable template）是C++14引入的新的一个种类的模板。可用于在名字空间作用域声明一个变量。例如：
template<class T>
constexpr T pi = T(3.1415926535897932385);  // variable template
template<class T>
T circular_area(T r) // function template
{
   return pi<T> * r * r; // pi<T> is a variable template instantiation
}
可以在类作用域声明一个静态数据成员： 
struct matrix_constants
{
   template<class T>
   using pauli = hermitian_matrix<T, 2>; // alias template
   template<class T> static constexpr pauli<T> sigma1 = { { 0, 1 }, { 1, 0 } }; // static data member template
   template<class T> static constexpr pauli<T> sigma2 = { { 0, -1i }, { 1i, 0 } };
   template<class T> static constexpr pauli<T> sigma3 = { { 1, 0 }, { 0, -1 } };
};
类的静态数据成员模板，也可以用类模板的非模板数据成员来实现：
struct limits {
   template<typename T>
   static const T min; // declaration of a static data member template
};
template<typename T> const T limits::min = { }; // definition of a static data member template
template<class T> class X {
    static T s; // declaration of a non-template static data member of a class template
};
template<class T> T X<T>::s = 0; // definition of a non-template data member of a class template

变量模板不能用作模板的模板参数（template template arguments）。

模板的使用

使用模板时，可以在模板名字后面显式给出用尖括号括住的模板实参列表（template argument list）。对模板函数或类的模板成员函数，也可不显式给出模板实参，而是由编译器根据函数调用的上下文推导出模板实参，这称为模板参数推导。
如果模板参数使用其默认值，则在模板实参列表中可以忽略它。如果所有的模板参数都使用了默认值，模板实参列表为空，但仍然必须写出成对的尖括号。例如：
template<class T = int> class X { };
X<> a; //编译通过X b;   //编译报错
对于作为类型的模板实参，不允许是局部类型（local type）、无链接性的类型（type with no linkage）、无名类型（unnamed type）或包括了这三种情形的复合类型。但C++11以及允许本地类型作为模板实参。

函数模板

以下以取最大值的函数模板maximum为例。此函数在编译时会自动产生对应参数类型的代码，而不用显式声明。
#include <iostream>template <typename T>inline const T& maximum(const T& x,const T& y){
   if(y > x){
      return y;
   }
   else{
      return x;
   }}int main(void){
   using namespace std;
   int a=3,b=7;
   float x=3.0,y=7.0;
   //Calling template function
   std::cout << maximum<int>(a,b) << std::endl;         //输出 7
   std::cout << maximum(a, b) << std::endl;             //自动补充类型声明
   std::cout << maximum<double>(x,y) << std::endl;  //输出 7
   return 0;}

模板的嵌套：成员模板

对于类中的模板成员函数、嵌套的成员类模板，可以在封闭类的内部或外部定义它们。当模板成员函数、嵌套类模板在其封闭类的外部定义时，必须以封闭类模板的模板参数（如果它们也是模板类）和成员模板的模板参数开头。如下例：
template <typename C> class myc{
  public:
    template <typename S> C foo(S s);};//下行需要给出外部类与内部嵌套类的模板形参列表：template<typename C> template <typename S> C myc<C>::foo(S s){C var;return var;   
}int main(){float f;myc<int> v1;v1.foo(f);}
C++标准规定：如果外围的类模板没有特例化，里面的成员模板就不能特例化。例如：
template <class T1> class A {
  template<class T2> class B {
      template<class T3> void mf1(T3);
      void mf2();
  };};template <> template <class X>
   class A<int>::B {
      template <class T> void mf1(T);
   };template <> template <> template<class T>
    void A<int>::B<double>::mf1(T t) { }template <class Y> template <>
     void A<Y>::B<double>::mf2() { } // ill-formed; B<double> is specialized but its enclosing class template A is not

别名模板

别名模板（aliase template）是C++11引入的技术。在C++03标准中，可以用typedef给全特化模板定义新的类型名。但是不允许用typedef施加于偏特化模板上。例如： 
template <typename First, typename Second, int Third>class SomeType;template <typename Second>typedef SomeType<OtherType, Second, 5> TypedefName; // Illegal in C++03
C++11增加了给偏特化模板增加别名的功能，例如： 
template <typename First, typename Second, int Third>class SomeType;template <typename Second>using TypedefName = SomeType<OtherType, Second, 5>;
using在C++11中也可用于其他的类型别名的声明:
typedef void (*FunctionType)(double);       // Old styleusing FunctionType1 = void (*)(double); // New introduced syntax