Skip to content

类模板

类模板和函数模板的定义和使用类似。有时 有两个或多个类其功能是相同的,仅仅是数据类型不同。

类模板用于实现类所需数据的类型参数化
cpp
#include <iostream>
using namespace std;

template<class NameType, class AgeType>
class Person {
public:
    Person(NameType name, AgeType age) {
        this->mName = name;
        this->mAge = age;
    }
    void showPerson() {
        cout << "name: " << this->mName << " age: " << this->mAge << endl;
    }

public:
    NameType mName;
    AgeType mAge;
};

void test01() {
    //Person P1("德玛西亚",18); // 类模板不能进行类型自动推导
    Person<string, int> P1("德玛西亚", 18);
    P1.showPerson();
}

int main(int argc, char *argv[]) {
    test01();

    return 0;
}

程序输出:

shell
name: 德玛西亚 age: 18

类模板和函数模板的区别

  • 类模板不可以使用自动类型推导,只能显式指定类型
  • 类模板中可以有默认参数

类模板中成员函数的创建时机

类模板中的成员函数并不是一开始就创建的,而是在程序运行时确定了T的数据类型才去创建

类模板做函数参数

  1. 指定传入类型

    void dowork(Person<string,int> &p){}

  2. 参数模板化

    cpp
    template <class T1, class T2>
    void dowork(Person<T1, T2> &p){}
  3. 整个类模板化

    cpp
    template <class T>
    void dowork(T &p){}

查看T的数据类型的方法typeid(T).name();

代码示例
cpp
#include <iostream>
using namespace std;
#include <string>

// 类模板的基本概念 下面定义一个类,使用模板技术进行修饰;然后test01函数进行使用
//template<class NameType, class AgeType>
template<typename NameType, typename AgeType>
class Person {
public:
    Person(NameType name, AgeType age) {
        this->c_Name = name;
        this->c_Age = age;
    }

public:
    NameType c_Name;
    AgeType c_Age;
};

void test01(void) {
    // Person p1("张楚岚", 19);//类模板不能进行隐式的自动类型推导
    Person<string, int> p1("张楚岚", 19);//只能显式指定实参的数据类型
    cout << p1.c_Name << "、" << p1.c_Age << endl;
}

//知识点02:类模板做函数参数
//a.指定传入类型
void test02(Person<string, int>& p1) {
    cout << p1.c_Name << "、" << p1.c_Age << endl;
}
//b.参数模板化
template<class NameType, class AgeType>
void test03(Person<NameType, AgeType>& p1) {
    cout << p1.c_Name << "、" << p1.c_Age << endl;
}
//c.整个类模板化
template<class T>
void test04(T &p1) {
    cout << p1.c_Name << "、" << p1.c_Age << endl;
}

int main(int argc, char *argv[]) {
    test01();

    Person<string, int> p1("张楚岚", 18);
    test02(p1);

    Person<string, int> p2("张楚岚", 17);
    test03(p2);

    Person<string, int> p3("张楚岚", 16);
    test04(p3);

    return 0;
}

程序输出:

shell
张楚岚、19
张楚岚、18
张楚岚、17
张楚岚、16

类模板派生普通类

必须要指定出父类中的T数据类型,才能给子类分配内存,一种方式是在做派生定义的时间显式制定父类中模板参数的类型,另一种方式是在派生定义的时间再次将父类中的模板参数模板化。

示例代码01
cpp
#include <iostream>

template<class T>
class Base {
public:
    Base() {}

    Base(T a) {
        this->m_a = a;
    }

public:
    T m_a;
};


// 第1种方式:继承的时间显式指定父类中模板参数的类型
class Sub1 : public Base<int> {
public:
    Sub1() {}

    Sub1(int a, int b) {
        this->m_a = a;
        this->m_b = b;
    }

public:
    int m_b;
};

// 第2种方式:继承的时间给父类中模板参数传入一个模板
template<class T>
class Sub2 : public Base<T> {
public:
    Sub2() {}

    Sub2(T a, int b) {
        this->m_a = a;
        this->m_b = b;
    }

public:
    int m_b;
};

template<class T1, class T2>
class Sub3 : public Base<T1> {
public:
    Sub3() {}

    Sub3(T1 a, T2 b) {
        this->m_a = a;
        this->m_b = b;
    }

public:
    T2 m_b;
};

int main(int argc, char *argv[]) {
    Sub1 sub1(5, 10);
    std::cout << "Sub1{a=[" << sub1.m_a << "]," << "b=[" << sub1.m_b << "]}" << std::endl;

    Sub2<int> sub2(6, 16);
    std::cout << "Sub2{a=[" << sub2.m_a << "]," << "b=[" << sub2.m_b << "]}" << std::endl;

    Sub3<int, int> sub3(7, 17);
    std::cout << "Sub3{a=[" << sub3.m_a << "]," << "b=[" << sub3.m_b << "]}" << std::endl;
    return 0;
}

示例代码02
cpp
#include <iostream>
using namespace std;

//类模板
template<class T>
class MyClass {
public:
    MyClass(T property) {
        this->mProperty = property;
    }

public:
    T mProperty;
};

//子类实例化的时候需要具体化的父类,子类需要知道父类的具体类型是什么样的
//这样C++编译器才能知道给子类分配多少内存

//普通派生类
class SubClass : public MyClass<int> {
public:
    SubClass(int b) : MyClass<int>(20) {
        this->mB = b;
    }

public:
    int mB;
};

int main(int argc, char *argv[]) {

    return 0;
}

类模板派生类模板

示例代码
cpp
#include <iostream>
using namespace std;

//父类类模板
template<class T>
class Base {
    T m;
};

template<class T>
class Child2 : public Base<double>//继承类模板的时候,必须要确定基类的大小
{
public:
    T mParam;
};

void test02() {
    Child2<int> d2;
}

int main(int argc, char *argv[]) {
    test02();

    return 0;
}

关于派生类的问题如果感觉难以理解可以查看课堂代码及视频

类模板类内实现

示例代码
cpp
#include <iostream>
using namespace std;

template<class NameType, class AgeType>
class Person {
public:
    Person(NameType name, AgeType age) {
        this->mName = name;
        this->mAge = age;
    }
    void showPerson() {
        cout << "name: " << this->mName << " age: " << this->mAge << endl;
    }

public:
    NameType mName;
    AgeType mAge;
};

void test01() {
    //Person P1("德玛西亚",18); // 类模板不能进行类型自动推导
    Person<string, int> P1("德玛西亚", 18);
    P1.showPerson();
}

int main(int argc, char *argv[]) {
    test01();

    return 0;
}

程序输出:

shell
name: 德玛西亚 age: 18

类模板类外实现

类模板类外实现的时间需要在作用域运算符::的前面指明模板参数,例如:void Person<T1, T2>::showPerson()

示例代码
cpp
#include <iostream>
using namespace std;
#include <string>

template<class T1, class T2>
class Person {
public:
    Person(T1 name, T2 age);
    void showPerson();

public:
    T1 mName;
    T2 mAge;
};

//类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
    this->mName = name;
    this->mAge = age;
}

template<class T1, class T2>
void Person<T1, T2>::showPerson() {
    cout << "Name:" << this->mName << " Age:" << this->mAge << endl;
}

void test() {
    Person<string, int> p("Obama", 20);
    p.showPerson();
}

int main(int argc, char *argv[]) {
    test();

    return 0;
}

程序输出:

shell
Name:Obama Age:20

代码位置

类模板中的成员函数不会一开始创建,因此导致分文件编写时只include头文件会出现连接不到函数实现的情况,出现无法解析的外部命令错误。

cpp
template<class T1, class T2>
class Person {
public:
    Person(T1 name, T2 age);
    void ShowPerson();

public:
    T1 mName;
    T2 mAge;
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
    this->mName = name;
    this->mAge = age;
}

template<class T1, class T2>
void Person<T1, T2>::ShowPerson() {
    cout << "Name:" << this->mName << " Age:" << this->mAge << endl;
}
#include <iostream>
using namespace std;
#include "Person.hpp"
#include <string>

//模板二次编译
//编译器编译源码 逐个编译单元编译的

int main(int argc, char *argv[]) {
    Person<string, int> p("Obama", 20);
    p.ShowPerson();

    return 0;
}

案例代码在Qt编译器顺利通过编译并执行,但是在Linux和Visual Studio编辑器下如果只包含头文件,那么会报错链接错误,需要包含cpp文件,但是如果类模板中有友元类,那么编译失败!

原因:

  • 类模板需要二次编译,在出现模板的地方编译一次,在调用模板的地方再次编译。
  • C++编译规则为独立编译。

解决方案:

类模板的声明和实现放到一个文件中,我们把这个文件命名为.hpp(这个是个约定的规则,并不是标准,必须这么写)。

模板类碰到友元函数

友元函数在类内实现的话基本上碰不到什么问题,具体情况请看下面的实例:

示例代码
cpp
#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2>
class Person {
    //1. 友元函数在类内实现
    friend void PrintPerson(Person<T1, T2> &p) {
        cout << "Name:" << p.mName << " Age:" << p.mAge << endl;
    }

public:
    Person(T1 name, T2 age) {
        this->mName = name;
        this->mAge = age;
    }

private:
    T1 mName;
    T2 mAge;
};

int main(int argc, char *argv[]) {
    Person<string, int> p("Jerry", 20);
    PrintPerson(p);

    return 0;
}

程序输出:

shell
Name:Jerry Age:20

但是如果友元函数是在类外实现,就会遇到一系列的问题:

程序编译的时间看到友元函数是一个普通函数,而在类外实现的时间却发现是一个模板函数,会出现错误

示例代码
cpp
#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2>
class Person {
    friend void PrintPerson(Person<T1, T2> &p);

public:
    Person(T1 name, T2 age) {
        this->mName = name;
        this->mAge = age;
    }

private:
    T1 mName;
    T2 mAge;
};

template<class T1, class T2>
void PrintPerson(Person<T1, T2> &p) {
    cout << "Name:" << p.mName << " Age:" << p.mAge << endl;
}

int main(int argc, char *argv[]) {
    Person<string, int> p("Jerry", 20);
    PrintPerson(p);

    return 0;
}

此时就需要在类内友元函数声明的地方加上一对尖括号来告诉编译器,这个友元函数是一个模板函数

cpp
friend void PrintPerson<>(Person<T1, T2> &p);

加上以后进行编译,错误又会变成另外一个:你说这是一个模板函数,我没有见到函数的样子啊!此时就需要在类前面对友元函数进行函数声明。

cpp
template<class T1, class T2>
void PrintPerson(Person<T1, T2> &p);

再次编译,错误又变了:编译器找不到Person类。此时就需要在前面再对类进行声明

cpp
template<class T1, class T2>
class Person;

经过以上一系列操作,程序就可以正常运行了。最终程序代码及运行情况如下:

点击查看完整代码
cpp
#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2>
class Person;

template<class T1, class T2>
void PrintPerson(Person<T1, T2> &p);

template<class T1, class T2>
class Person {
    friend void PrintPerson<>(Person<T1, T2> &p);

public:
    Person(T1 name, T2 age) {
        this->mName = name;
        this->mAge = age;
    }

private:
    T1 mName;
    T2 mAge;
};

template<class T1, class T2>
void PrintPerson(Person<T1, T2> &p) {
    cout << "Name:" << p.mName << " Age:" << p.mAge << endl;
}

int main(int argc, char *argv[]) {
    Person<string, int> p("Jerry", 20);
    PrintPerson(p);

    return 0;
}

程序输出:

shell
Name:Jerry Age:20

上面的步骤太繁琐了,有一个简便的思路就是将类外实现的友元函数声明及定义在一起实现,都在类的前面进行实现:

点击查看完整代码
cpp
#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2>
class Person;

template<class T1, class T2>
void PrintPerson(Person<T1, T2> &p) {
    cout << "Name:" << p.mName << " Age:" << p.mAge << endl;
}

template<class T1, class T2>
class Person {
    friend void PrintPerson<>(Person<T1, T2> &p);

public:
    Person(T1 name, T2 age) {
        this->mName = name;
        this->mAge = age;
    }

private:
    T1 mName;
    T2 mAge;
};

int main(int argc, char *argv[]) {
    Person<string, int> p("Jerry", 20);
    PrintPerson(p);

    return 0;
}
cpp
#include <iostream>
using namespace std;
#include <string>

template<class T1, class T2>
class Person;
//告诉编译器这个函数模板是存在
template<class T1, class T2>
void PrintPerson2(Person<T1, T2>& p);

//友元函数在类内实现
template<class T1, class T2>
class Person {
    //1. 友元函数在类内实现
    friend void PrintPerson(Person<T1, T2>& p) {
        cout << "Name:" << p.mName << " Age:" << p.mAge << endl;
    }

    //2.友元函数类外实现
    //告诉编译器这个函数模板是存在
    friend void PrintPerson2<>(Person<T1, T2>& p);

    //3. 类模板碰到友元函数模板
    template<class U1, class U2>
    friend void PrintPerson(Person<U1, U2>& p);

public:
    Person(T1 name, T2 age) {
        this->mName = name;
        this->mAge = age;
    }
    void showPerson() {
        cout << "Name:" << this->mName << " Age:" << this->mAge << endl;
    }

private:
    T1 mName;
    T2 mAge;
};

void test01() {
    Person<string, int> p("Jerry", 20);
    PrintPerson(p);
}

// 类模板碰到友元函数
//友元函数类外实现  加上<>空参数列表,告诉编译去匹配函数模板
template<class T1, class T2>
void PrintPerson2(Person<T1, T2>& p) {
    cout << "Name2:" << p.mName << " Age2:" << p.mAge << endl;
}

void test02() {
    Person<string, int> p("Jerry", 20);
    PrintPerson2(p);//不写可以编译通过,写了之后,会找PrintPerson2的普通函数调用,因为写了普通函数PrintPerson2的声明
}

int main(int argc, char *argv[]) {
    test01();
    test02();

    return 0;
}

程序输出:

shell
Name:Jerry Age:20
Name2:Jerry Age2:20

类模板的应用

设计一个数组模板类MyArray,完成对不同类型元素的管理

示例代码
cpp
template<class T>
class MyArray {
public:
    explicit MyArray(int capacity) {
        this->m_Capacity = capacity;
        this->m_Size = 0;
        // 如果T是对象,那么这个对象必须提供默认的构造函数
        pAddress = new T[this->m_Capacity];
    }

    //拷贝构造
    MyArray(const MyArray &arr) {
        this->m_Capacity = arr.m_Capacity;
        this->m_Size = arr.m_Size;
        this->pAddress = new T[this->m_Capacity];
        for (int i = 0; i < this->m_Size; i++) {
            this->pAddress[i] = arr.pAddress[i];
        }
    }

    //重载[] 操作符  arr[0]
    T &operator[](int index) {
        return this->pAddress[index];
    }
    //尾插法
    void Push_back(const T &val) {
        if (this->m_Capacity == this->m_Size) {
            return;
        }
        this->pAddress[this->m_Size] = val;
        this->m_Size++;
    }
    void Pop_back() {
        if (this->m_Size == 0) {
            return;
        }
        this->m_Size--;
    }
    int getSize() {
        return this->m_Size;
    }
    //析构
    ~MyArray() {
        if (this->pAddress != NULL) {
            delete[] this->pAddress;
            this->pAddress = NULL;
            this->m_Capacity = 0;
            this->m_Size = 0;
        }
    }

private:
    T *pAddress;   //指向一个堆空间,这个空间存储真正的数据
    int m_Capacity;//容量
    int m_Size;    // 大小
};


//测试代码:
class Person {
public:
    Person() {}
    Person(string name, int age) {
        this->mName = name;
        this->mAge = age;
    }

public:
    string mName;
    int mAge;
};


void PrintMyArrayInt(MyArray<int> &arr) {
    for (int i = 0; i < arr.getSize(); i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

void PrintMyPerson(MyArray<Person> &personArr) {
    for (int i = 0; i < personArr.getSize(); i++) {
        cout << "姓名:" << personArr[i].mName << " 年龄: " << personArr[i].mAge << endl;
    }
}

MyArray<int> myArrayInt(10);
for (int i = 0; i < 9; i++) {
    myArrayInt.Push_back(i);
}
myArrayInt.Push_back(100);
PrintMyArrayInt(myArrayInt);

MyArray<Person> myArrayPerson(10);
Person p1("德玛西亚", 30);
Person p2("提莫", 20);
Person p3("孙悟空", 18);
Person p4("赵信", 15);
Person p5("赵云", 24);
myArrayPerson.Push_back(p1);
myArrayPerson.Push_back(p2);
myArrayPerson.Push_back(p3);
myArrayPerson.Push_back(p4);
myArrayPerson.Push_back(p5);

自己动手实现动态数组的封装及测试

点击查看完整代码
cpp
#ifndef _DYNAMICARRAY_H
#define _DYNAMICARRAY_H

#include <cstring>

template<class T>
class dynamicArray {
public:
    dynamicArray();

    dynamicArray(int capacity);

    dynamicArray(dynamicArray &array);

    void add(const T &element);

    void foreach (void (*printMethod)(T &data));

    T &operator[](int index);

    ~dynamicArray();

private:
    void checkCapacity(int capacity);

private:
    T *array;
    int m_capacity;
    int m_size;
};

template<class T>
dynamicArray<T>::dynamicArray() {
    this->m_capacity = 10;
    this->array = new T[this->m_capacity];
    this->m_size = 0;
}

template<class T>
dynamicArray<T>::dynamicArray(int capacity) {
    checkCapacity(capacity);
    this->array = new T[this->m_capacity];
    this->m_size = 0;
}

template<class T>
dynamicArray<T>::dynamicArray(dynamicArray &array) {
    this->m_capacity = array.m_capacity;
    if (this->array) {
        delete[] this->array;
        this->array = new T[this->m_capacity];
    }
    this->m_size = array.m_size;
}

template<class T>
void dynamicArray<T>::add(const T &element) {
    checkCapacity(this->m_capacity);
    this->array[this->m_size] = element;
    this->m_size++;
}

template<class T>
void dynamicArray<T>::foreach (void (*printMethod)(T &)) {
    if (printMethod == nullptr) {
        return;
    }

    for (int i = 0; i < this->m_size; ++i) {
        printMethod(this->array[i]);
    }
    std::cout << std::endl;
}

template<class T>
T &dynamicArray<T>::operator[](int index) {
    return this->array[index];
}

template<class T>
dynamicArray<T>::~dynamicArray() {
    if (this->array) {
        delete[] this->array;
        this->array = nullptr;
    }
    this->m_capacity = 0;
    this->m_size = 0;
    std::cout << "数组已析构" << std::endl;
}

template<class T>
void dynamicArray<T>::checkCapacity(int capacity) {
    if (capacity > this->m_capacity)
        this->m_capacity = capacity;
    if (this->m_capacity <= this->m_size) {
        int newCapacity = this->m_capacity + (this->m_capacity >> 1);
        T *array = new T[newCapacity];
        memcpy(array, this->array, sizeof(T *) * this->m_size);
        delete[] this->array;
        this->array = array;
        this->m_capacity = newCapacity;
    }
}

#endif//_DYNAMICARRAY_H
#include "dynamicArray.hpp"
#include <iostream>
#include <string>

struct Person {
    Person() {}

    Person(const std::string &name, int age) : name(name), age(age) {}

    Person(Person &p) {
        this->name = p.name;
        this->age = p.age;
    }

    std::string name;
    int age;
};

void printInt(int &data) {
    int p = data;
    std::cout << p << " ";
}

void printPerson(Person &data) {
    Person p = data;
    std::cout << p.name << " " << p.age << "   ";
}

void printPerson(Person *&data) {
    Person *p = data;
    std::cout << (*p).name << " " << (*p).age << "   ";
}

int main(int argc, char *argv[]) {
    dynamicArray<int> array(10);
    array.add(10);
    array.add(20);
    array.add(30);
    array.foreach (printInt);
    for (int i = 0; i < 100; ++i)
        array.add(i);
    array.foreach (printInt);

    Person p1("张三", 13);
    Person p2("李四", 14);
    Person p3("王五", 15);
    dynamicArray<Person> p_array(10);
    p_array.add(p1);
    p_array.add(p2);
    p_array.add(p3);
    p_array.foreach (printPerson);

    Person *p4 = new Person;
    p4->name = "赵六";
    p4->age = 16;
    Person *p5 = new Person("周七", 17);
    dynamicArray<Person *> pp_array(5);
    pp_array.add(p4);
    pp_array.add(p5);
    pp_array.foreach (printPerson);

    std::cout << array[4] << std::endl;
    return 0;
}

类模板全特化

全特化就是所有的模板参数都使用具体的类型进行代表。特化版本会被编译器优先选择。

常规全特化

下面我们从一个泛化版本开始来一点一点的介绍特化,请看下面一个类模板的泛化

点击查看代码
cpp
#include <iostream>
using namespace std;

template <typename T1, typename T2>
class TestClass {
public:
    TestClass() {
        cout << "泛化版本构造函数" << endl;
    }
    void func() {
        cout << "泛化版本func成员函数" << endl;
    }
};

int main(int argc, char *argv[]) {
    TestClass<int, int> tc1;
    tc1.func();
    TestClass<char, char> tc2;
    tc2.func();
    TestClass<double, double> tc3;
    tc3.func();

    return 0;
}

程序输出:

shell
泛化版本构造函数
泛化版本func成员函数
泛化版本构造函数
泛化版本func成员函数
泛化版本构造函数
泛化版本func成员函数

可以看到,定义了一个模板类以后,无论是两个int类型的调用还是两个double类型的调用都是可以成功的,那么如果我们想让调用两个int类型的时间去执行特殊的操作而不是类模板里面的操作的时间,就需要对模板进行特化了,具体实现请看下面程序

点击查看代码
cpp
#include <iostream>
using namespace std;

template <typename T1, typename T2>
class TestClass {
public:
    TestClass() {
        cout << "泛化版本构造函数" << endl;
    }
    void func() {
        cout << "泛化版本func成员函数" << endl;
    }
};

//对int、int类型进行特化,特化的是整个类模板
template <>
class TestClass<int, int> {
public:
    TestClass() {
        cout << "int, int特化版本构造函数" << endl;
    }
    void func() {
        cout << "int, int特化版本func成员函数" << endl;
    }
};
//特化操作结束

int main(int argc, char *argv[]) {
    TestClass<int, int> tc1;
    tc1.func();
    TestClass<char, char> tc2;
    tc2.func();
    TestClass<double, double> tc3;
    tc3.func();

    return 0;
}

程序输出:

shell
int, int特化版本构造函数
int, int特化版本func成员函数
泛化版本构造函数
泛化版本func成员函数
泛化版本构造函数
泛化版本func成员函数

上面对模板进行特化的时间将所有的模板参数(T1T2)都进行了显式的指定,这种方式的特化就是类模板的全特化。

特化成员函数而不是模版

如果我们想特化的只是类中的成员函数而不是整个类模板,可以使用下面这种形式

点击查看代码
cpp
#include <iostream>
using namespace std;

template <typename T1, typename T2>
class TestClass {
public:
    TestClass() {
        cout << "泛化版本构造函数" << endl;
    }
    void func() {
        cout << "泛化版本func成员函数" << endl;
    }
};

template <>
class TestClass<int, int> {
public:
    TestClass() {
        cout << "int, int特化版本构造函数" << endl;
    }
    void func() {
        cout << "int, int特化版本func成员函数" << endl;
    }
};

//对成员函数进行float特化
template <>
void TestClass<float, float>::func() {
    cout << "float特化版本-成员函数特化" << endl;
}
//成员函数特化操作结束

int main(int argc, char *argv[]) {
    TestClass<int, int> tc1;
    tc1.func();
    TestClass<char, char> tc2;
    tc2.func();
    TestClass<double, double> tc3;
    tc3.func();
    TestClass<float, float> tc4;
    tc4.func();

    return 0;
}

程序输出:

shell
int, int特化版本构造函数
int, int特化版本func成员函数
泛化版本构造函数
泛化版本func成员函数
泛化版本构造函数
泛化版本func成员函数
泛化版本构造函数
float特化版本-成员函数特化

可以看到,通过上面的形式成功的对成员函数进行了全特化操作,程序运行时float创建的是泛化版本的对象调用了泛化版本的构造函数,但是执行func函数时执行的是我们为float类型全特化的成员函数。

类模板偏特化

类模板偏特化要从两个方面说起,一个是模板参数数量上,一个是模板参数范围上。

模板参数数量

模板参数数量的偏特化是指模板参数没有全部进行特化,只对其中一部分进行特化,另一部分保持泛化类型,具体请看下面的例子

点击查看代码
cpp
#include <iostream>
using namespace std;

template <typename T1, typename T2, typename T3>
class TestClass {
public:
    TestClass() {
        cout << "泛化版本构造函数" << endl;
    }
    void func() {
        cout << "泛化版本func成员函数" << endl;
    }
};

template <typename T2>  //只写一个泛化类型T2是因为T1和T3分别被我特化成了int,double类型
class TestClass<int, T2, double> {
public:
    TestClass() {
        cout << "<int, 泛化类型, double>偏特化版本构造函数" << endl;
    }
    void func() {
        cout << "<int, 泛化类型, double>偏特化版本func成员函数" << endl;
    }
};


int main(int argc, char *argv[]) {
    TestClass<int, int, int> tc1;
    tc1.func();
    TestClass<int, int, double> tc2;
    tc2.func();

    return 0;
}

程序输出:

shell
泛化版本构造函数
泛化版本func成员函数
<int, 泛化类型, double>偏特化版本构造函数
<int, 泛化类型, double>偏特化版本func成员函数

上面的例子中模板参数有三个,但是只对其中的两个进行了特化操作,这就是偏特化(通过对模板参数数量方面进行的偏特化)。

模板参数范围

模板参数范围是指同种类型或相似类型的变化,比如int类型到const int类型;int类型到int*类型;从int类型到int&类型,从int类型到int&&类型,都是相似类型上的范围变化。

可以从下面的实例进行理解

点击查看代码
cpp
#include <iostream>
using namespace std;

template <typename T>
class TestClass {
public:
    TestClass() {
        cout << "泛化版本构造函数" << endl;
    }
    void func() {
        cout << "泛化版本func成员函数" << endl;
    }
};

template <typename T>
class TestClass<T*> {
public:
    TestClass() {
        cout << "T*偏特化版本构造函数" << endl;
    }
    void func() {
        cout << "T*偏特化版本func成员函数" << endl;
    }
};

int main(int argc, char *argv[]) {
    TestClass<int> tc1;
    tc1.func();
    TestClass<int*> tc2;
    tc2.func();
    TestClass<char*> tc3;
    tc3.func();

    return 0;
}

程序输出:

shell
泛化版本构造函数
泛化版本func成员函数
T*偏特化版本构造函数
T*偏特化版本func成员函数
T*偏特化版本构造函数
T*偏特化版本func成员函数

可以看到,我们针对于T*进行了特化的操作,int*double*都可以对其进行调用。我们可以理解为我们虽然对泛型T特化到了T*,但是其本质上仍然是一个泛型,没有被我们特化到固定的类型上,它所限定的只是一个范围,所以仍然可以使用泛型的赋值。