1.类模板的定义


类模板的语法形式
1
2
3
4
5
6
7
8
9
template <typename 类型参数1, typename 类型参数2,...>

template <typename A, typename b, typename C>
class MyClass
{
public:
    A m_a;
    b foo(C c);
};
类型参数

在类模板内部,类型参数可以像其它任何具体类型一样,用于成员变量、成员函数、成员类型(内部类型),甚至基类的说明

1
2
3
4
5
6
7
template<typename M, typename R, typename A, typename V, typename T, typename B>
class MyClass : public
{
    M m_mem;
    R function(A arg){... V var ...}
    typename T* pointer;
};

2.类模板的使用


类模板的两部实例化
  • 从类模板到对象实际上经历了两个实例化过程
  • 编译期:编译器将类模板实例化为类并生成对象创建指令
  • 运行期:处理器执行对象创建指令,将类实例化为内存对象
  • 类模板本身并不代表一个确定的类型,既不能用于定义对象,也不能用于声明指针或引用。只有通过模板实参将其实例化为具体类以后,可具备类型语义
调用谁实例化谁

类模板中,只有那些被调用的成员函数才会被实例化,即产生实例化代码。某些类型虽然并没有提供类模板所需要的功能,但照样可以实例化该类模板,只要不直接或间接调用那些依赖于未提供功能的成员函数即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
template<typename T> class Comparator
{
    T const& max()const
    {
        return m_x < m_y? m_y : m_x;
    }
    T const& min()
    {
        return m_x > m_y ? m_y : m_x;
    }
};
class Integer
{
    bool operator<(Integer const& rhs);
};
Comparator<Integer> c1(123, 456);
cout << c1.max() << endl;
类模板不支持隐式推断
  • 与函数模板不同,类模板的模板参数不支持隐式推断
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
template<typename T>
class Comparator
{
    int m_x, m_y;
public:
    Comparator(T const& x, T const& y):m_x(x), m_y(y){}
};

Comparator c1(123, 456); // 错误
Comparator<int> c2(123, 456);

3.静态成员与递归实例化


类模板的静态成员

类模板的静态成员变量,既不是一个对象一份,也不是一个模板一份,而是在该类模板的每个实例化类中,各有一份独立的拷贝,且为该类的所有实例化对象所共享

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
template<typename T> class A
{
public:
    static void print()
    {
        cout << &m_i << " " << &m_t << endl;
    }
private:
    static int m_i;
    static T m_t;
};
template<typename T> int A<T>::m_i;
template<typename T> T A<T>::m_t;
类模板的递归实例
  • 类模板的类型可以是任何类型,只要该类型能够提供模板所需要的功能
  • 模板自身的实例化类亦可以实例化其自身,称作递归实例化。通过这种方法可以很容易的构建那些在空间上具有递归特性的数据结构
1
2
3
4
5
6
7
8
template<typename T>
class Array
{
    T m_array[3];
};
Array<int> a1; // 一维数组
Array<Array<int> > a2; // 二维数组
Array<Array<Array<int> > > a3; // 三维数组

4.类模板的特化


全类特化

特化一个类模板可以特化该类模板的所有成员函数,相当于重写一个针对某些特定类型的具体类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
template<> class Comparator<char const*>
{
    char const* const& m_x, m_y;
public:
    Comparator (char const* const& x, char const* const& y):
        m_x(x), m_y(y) {}
    char const* const& max() const
    {
        return strcmp(m_x, m_y) < 0 ? m_y : m_x;
    }
};
Comparator<char const*> cmp("hello", "world");
cout << cmp.max() << endl; // world
成员特化

  1. 类模板可以只针对部分成员函数进行特化
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
template<typename T> class Comparator
{
    T const& m_x, &m_y;
public:
    Comparator(T const& x, T const& y):
        m_x(x), m_y(y) {}
    T const& max() const
    {
        return m_x < m_y ? m_y : m_x;
    }
};
template<> char const* const& Comparator<char const*>::max() const
{
    return strcmp(m_x, m_y) < 0 ? m_y : m_x;
}

5.局部特化


  1. 对部分目标参数自行指定

类模板可以被局部特化,即一方面为类模板指定特定的实现,另一方面又允许用户对部分模板参数自行指定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
partial.cpp
template<typename A, typename B> class X{};
// 针对第二个模板参数取short的局部特化
template<typename A> class X<A, short> {};
// 针对两个模板参数取相同类型的局部特化
template<typename A> class X<A, A>{};
// 针对两个模板参数取某种类型指针的局部特化
template<typename A, typename B> class X<A*, B*> {};
// 针对两个模板参数取某种引用类型的局部特化
template<typename A, template B> class X<A&, B&> {};
// 针对两个模板参数取某种类型数组的局部特化
template<typename A, typename B> class X<A[], B[]> {};
  1. 同等程度的特化匹配导致歧义

如果多个局部特化同等程度地匹配某个声明,那么该声明将因二义性而导致歧义错误

1
2
3
4
5
6
X<int*, int*> x; // 错误
    template<typename A> class X<A, A> {};
    template<typename A, typename B> class X<A*, B*> {};
// 除非有更好的匹配
X<int*, int*> X;
    template<typename A> class X<A*, A*> {};

6.类模板的缺省值


  1. 类模板可以带有缺省值
  • 类模板的模板参数可以带有缺省值,即缺省模板实参
  • 实例化类模板时,如果提供了模板实参则用所提供的模板实参实例化相应的模板形参,如果没有提供模板实参则相应的模板形参取缺省值
1
2
3
4
template<typename A = int, typename B = double, typename C = string>class X {...};
X<char, short> x1(...);// X<char, short, string> x1;
X<char> x2(...); // X<char, double, string> x2;
X<> x3(...); // X<int, double, string> x3;

如果类模板的某个模板参数带有缺省值,那么它后面的所有模板参数必须带有缺省值。

  1. 后面的参数可以引用前面的参数
  • template<typename A, typename B = A*> class X{…};
  • X x; // X<int, int*> x;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
template<typename T, typename C = vector<T> >
class Stack
{
public:
    void push(T const& elem)
    {
        m_container.push_bask(elem);
    }
private:
C m_container;
};

非类型模板参数


  1. 普通数值作为模板参数

数值类型的参数并不局限于类型参数,普通数值也可以作为模板参数,前面不要写typename,而要写具体类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<typename T, size_t C> class Array
{
T m_array[C];
public:
    T& operator[] (size_t i)
    {
        return m_array[i];
    }
    T const& operator[] (size_t i) const
    {
        return const_cast<Array<T>&> (* this) [i];
    }
    size_t capacity() const
    {
        return C;
    }
};
Array<int, 10> array;
for(size_t i = 0; i < array.capacity(); ++i)
{
    array[i] = i + 1;
}
  1. 非类型模板参数只能是常量

非类型模板参数只能是常量,常量表达式,以及带有常量属性(const)的变量,但不能同时具有挥发性(volatile)

1
2
3
4
5
6
Array<int, 10> array;
Array<int, 3+7> array;
int const x = 3;, y = 7;
Array<int, x + y> array;
int const volatile x = 3, y = 7;
Array<int, x + y> array; // 错误❌