模板(Template)
指C++程序设计设计语言中采用类型作为参数的程序设计,支持通用程序设计。C++ 的标准库提供许多有用的函数大多结合了模板的观念,如STL以及IO Stream。
函数模板
函数模板定义一族函数。
//template1.cpp #include <iostream>
template<typename T> void swap(T &a, T &b) {
T tmp{a}; a = b;
b = tmp;
}
int main(int argc, char* argv[])
{
int a = 2; int b = 3;
swap(a, b); // 使用函数模板
std::cout << "a=" << a << ", b=" << b << std::endl;
double c = 1.1; double d = 2.2; swap(c, d);std::cout << "c=" << c << ", d=" << d << std::endl;return 0;
}
函数模板的格式:
template<parameter-list> function-declaration
函数模板在形式上分为两部分:模板、函数。在函数前面加上 template<…>就成为函数模板,因此对函数的各种修饰(inline、constexpr 等)需要加在 function-declaration 上,而不是 template 前。如
template
inline T min(const T &, const T &);
上面 swap 函数模板,使用了类型形参。函数模板就像是一种契约,任何满足该契约的类型都可以做为模板实参。而契约就是函数实现中,模板实参需要支持的各种操作。上面
swap 中 T 需要满足的契约为:支持拷贝构造和赋值。
C++ 的函数模板本质上函数的重载,泛型只是简化了程序员的工作,让这些重载通过编译器来完成
函数模板不是函数
刚才我们提到函数模板用来定义一族函数,而不是一个函数。C++是一种强类型的语
言,在不知道 T 的具体类型前,无法确定 swap 需要占用的栈大小(参数栈,局部变量), 同时也不知道函数体中 T 的各种操作如何实现,无法生成具体的函数。只有当用具体
类型去替换 T 时,才会生成具体函数,该过程叫做函数模板的实例化。当在 main 函数中调用 swap(a,b)时,编译器推断出此时 T 为 int,然后编译器会生成 int 版的 swap 函数供调用。所以相较普通函数,函数模板多了生成具体函数这一步。如果我们只是编
写了函数模板,但不在任何地方使用它(也不显式实例化),则编译器不会为该函数模板生成任何代码。函数模板实例化分为隐式实例化和显式实例化。
隐式实例化
仍以 swap 为例,我们在main 中调用 swap(a,b)时,就发生了隐式实例化(implicit instantiation)。当函数模板被调用,且在之前没有显式实例化时,即发生函数模板的隐式实例化。如果模板实参能从调用的语境中推导,则不需要提供。效率较低。
//template2.cpp #include
template void print(const T &r) {
std::cout << r << std::endl;
}
int main() {
// 隐式实例化print(int) print(1);
// 实例化 print(char) print<>('c');
// 仍然是隐式实例化,我们希望编译器生成print(double) print(1);
return 0;
}