EasyC++42,模板实例化

160 阅读2分钟

大家好,我是梁唐。

这是EasyC++系列的第42篇,来聊聊模板实例化和具体化。

想要追求更好阅读体验的同学,可以点击访问github仓库:EasyLeetCode

实例化和具体化

关于函数模板,还有一个很重要的概念,就是实例化。

我们在编写代码时,如果只是编写了函数模板本身,编译器是不会为我们生成函数的定义的。当编译器使用模板为特定的类型生成函数定义时,就会得到一个模板的实例。这个概念有点像是Python里的元类,元类的实例是另外一个类。

比如我们定义了一个函数模板:

template <typename T>
void Swap(T &a, T &b) {
    T temp = a;
    a = b;
    b = temp;
}

当我们调用它,传入两个int类型的时候,编译器就会生成一个实例,这个实例使用的类型是int。当我们使用double类型的参数又一次调用的时候,编译器会继续生成double类型的实例。这个生成实例的过程是不可见的,所以被称为隐式实例化。

在早年的C++版本当中只支持隐式实例化,但现在C++允许显示实例化。也就意味着我们可以手动命令编译器创建特定的实例,比如Swap<int>()。语法是通过<>声明指定模板类型,并且在声明之前加上关键字template,如:

template void Swap<int>(int, int);

这个语法看起来和显式具体化非常相似,显式具体化的写法是:

template<> void Swap<int>(int &, int &);
template<> void Swap(int &, int &);

看起来非常相似,但是含义是完全不同的。显式具体化的含义是对于某特定类型不要使用原模板生成函数,而应专门使用指定的函数定义。而显式实例化是使用之前的模板函数的定义的,只不过是手动触发编译器创建函数实例而已。

对了,我们不能同时在一个文件中,使用同一种类型的显式实例化和显式具体化,这会引起报错。

我们如果死记显式实例化的声明,的确很容易和具体化混淆。但我们可以在代码当中直接使用,直接使用的形式则要简单许多,只需要通过<>表明类型即可。例如:

template <typename T>
T Add(T a, T b) {
    return a + b;
}

int main() {
	int a = 3;
	double b = 3.5;
	cout << Add<double>(a, b) << endl;
}   

在上面这段代码当中,我们通过给Add函数加上了<double>来手动创建了一个接受double类型的函数。需要注意的是,我们传入的a是一个int类型。所以编译器会执行强制类型转换,将它转换成double传入。