Skip to content

模板总结

模板机制剖析

思考

为什么函数模板可以和普通函数放在一起?C++编译器是如何实现函数模板机制的?

编译过程

hello.c程序是高级C语言程序,这种程序易于被人读懂。为了在系统上运行hello.c程序,每一条C语句都必须转化为低级的机器指令。然后将这些机器指令打包成可执行目标文件格式,并以二进制形式存储于磁盘中。

预处理(Pre-processing) ➡️ 编译(Compiling) ➡️ 汇编(Assembling) ➡️ 链接(Linking)

函数模板机制结论:

  • 编译器并不是把函数模板处理成能够处理任何类型的函数
  • 函数模板通过具体类型产生不同的函数——通过函数模板产生的函数 称为模板函数
  • 编译器会对函数模板进行,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

模板的局限性

模板并不是真实的通用,对于自定义数据类型,可以使用具体化技术,实现对自定义数据类型特殊使用。

假设有如下模板函数:

cpp
template<class T>
void f(T a, T b){

}

如果代码实现时定义了赋值操作 a = b;,但是T为数组,这种假设就不成立了

同样,如果里面的语句为判断语句 if (a > b),但T如果是结构体,该假设也不成立,另外如果是传入的数组,数组名为地址,因此它比较的是地址,而这也不是我们所希望的操作。

总之,编写的模板函数很可能无法处理某些类型,另一方面,有时候通用化是有意义的,但C++语法不允许这样做。为了解决这种问题,可以提供模板的重载,为这些特定的类型提供具体化的模板

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

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

//普通交换函数
template<class T>
void mySwap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

//使用具体化的模板技术,具体化的原型和定义以template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template<>
void mySwap<Person>(Person& p1, Person& p2) {
    string nameTemp;
    int ageTemp;

    nameTemp = p1.mName;
    p1.mName = p2.mName;
    p2.mName = nameTemp;

    ageTemp = p1.mAge;
    p1.mAge = p2.mAge;
    p2.mAge = ageTemp;
}

void test() {
    Person P1("Tom", 10);
    Person P2("Jerry", 20);

    cout << "P1 Name = " << P1.mName << " P1 Age = " << P1.mAge << endl;
    cout << "P2 Name = " << P2.mName << " P2 Age = " << P2.mAge << endl;
    mySwap(P1, P2);
    cout << "P1 Name = " << P1.mName << " P1 Age = " << P1.mAge << endl;
    cout << "P2 Name = " << P2.mName << " P2 Age = " << P2.mAge << endl;
}

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

    return 0;
}

程序输出:

shell
P1 Name = Tom P1 Age = 10
P2 Name = Jerry P2 Age = 20
P1 Name = Jerry P1 Age = 20
P2 Name = Tom P2 Age = 10

更详细的模板具体化技术使用参考函数模板和类模板章节中的模板特化部分。