[C++]C++Primer Chapter 16
模板与泛型编程
16.1 定义模板
16.1.1 函数模板
一个函数模板就是一个公式,可用来生成针对特定类型的函数版本。
模板定义以关键字template开始,后跟一个模板参数列表(template parameter list),这是一个逗号分隔的一个或多个模板参数(template parameter)的列表,用小于号(<)和大于号(>)包围起来。
模板参数表示在类或函数定义中用到的类型或值。当使用模板时,我们(隐式地或显式地)指定模板实参(template argument),将其绑定到模板参数上。
实例化函数模板
当我们调用一个函数模板时,编译器(通常)用函数实参来为我们推断模板实参。
编译器用推断出的模板参数来为我们实例化(instantiate)一个特定版本的函数。当编译器实例化一个模板时,它使用实际的模板实参代替对应的模板参数来创建出模板的一个新“实例”。
模板类型参数
类型参数前必须使用关键字class或typename
非类型模板参数
非类型模板参数的模板实参必须是常量表达式。
除了定义类型参数,还可以在模板中定义非类型参数(nontype parameter)。一个非类型参数表示一个值而非一个类型。我们通过一个特定的类型名而非关键字class或typename来指定非类型参数。
当一个模板被实例化时,非类型参数被一个用户提供的或编译器推断出的值所代替。这些值必须是常量表达式(参见2.4.4节,第58页),从而允许编译器在编译时实例化模板。
例如,我们可以编写一个compare版本处理字符串字面常量。这种字面常量是const char的数组。由于不能拷贝一个数组,所以我们将自己的参数定义为数组的引用(参见6.2.4节,第195页)。由于我们希望能比较不同长度的字符串字面常量,因此为模板定义了两个非类型的参数。第一个模板参数表示第一个数组的长度,第二个参数表示第二个数组的长度
一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或(左值)引用。绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期(参见第12章,第400页)。
inline和constexpr的函数模板
inline或constexpr说明符放在模板参数列表之后,返回类型之前
编写类型无关的代码
编写泛型代码的两个重要原则:
- 模板中的函数参数是const的引用。(支持不能拷贝的对象,处理大对象时效率更高)
- 函数体中的条件判断仅使用<比较运算。(降低对处理类型的要求)
模板编译
当编译器遇到一个模板定义时,它并不生成代码。只有当我们实例化出模板的一个特定版本时,编译器才会生成代码。
为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义。因此,与非模板代码不同,模板的头文件通常既包括声明也包括定义。
关键概念:模板和头文件
模板包含两种名字:
- 那些不依赖于模板参数的名字
- 那些依赖于模板参数的名字
当使用模板时,所有不依赖于模板参数的名字都必须是可见的,这是由模板的提供者来保证的。而且,模板的提供者必须保证,当模板被实例化时,模板的定义,包括类模板的成员的定义,也必须是可见的。
用来实例化模板的所有函数、类型以及与类型关联的运算符的声明都必须是可见的,这是由模板的用户来保证的。
通过组织良好的程序结构,恰当使用头文件,这些要求都很容易满足。模板的设计者应该提供一个头文件,包含模板定义以及在类模板或成员定义中用到的所有名字的声明。模板的用户必须包含模板的头文件,以及用来实例化模板的任何类型的头文件。
大多数编译错误在实例化期间报告