模板编程

模板

模板是C++中泛型编程的基础。一个模板就是一个创建类或函数的蓝图或公式,在使用模板时,我们提供足够的信息将模板转换为特定的类或函数。这种转换发生在编译时。

函数模板

考虑这样一个功能的实现:交换两个静态类型相同的变量的值,编写一个swap函数。

void swap(int &a, int &b) {
	int temp = a;
	a = b;
	b = temp;
}

上面的swap函数只能接受两个整型实参并交换它们,如果要实现更多类型变量的值交换,我们要写出很多个重载函数:swap(double &a, double &b) swap(char &a, char &b)……

在这里运用函数重载显然效率低下,我们希望定义一个通用的swap函数,能够接受多种类型的实参。用函数模板可以做到这一点:

template 
void swap(T &t1, T &t2) {
	T temp = t1;
	t1 = t2;
	t2 = temp;
}

如上,函数模板就是一个公式,可用来生成针对特定类型的函数版本。编译器(通常)根据函数实参的类型来为我们推断模板实参,并生成相对应类型的swap版本。这极大减少了代码编写的工作量,提高了开发效率。

非类型模板参数

上面的swap模板中,T属于模板类型参数。一般来说,我们可以将类型参数看作类型说明符,就像内置类型或类类型来使用:指定返回类型或函数的参数类型,以及函数体内的变量声明或类型转换等。

除了定义类型参数,还可以在模板中定义非类型参数(nontype parameter)。一个非类型参数表示一个值而非一个类型。我们通过一个特定的类型名而非关键字class或typename来指定非类型参数。

例如,编写一个处理字符串字面常量的大小比较函数。

template 
int compare(const char (&p1)[N], const char (&p2)[M]) {
	return strcmp(p1, p2);
}

当我们调用compare时:compare("hi", "mom") 编译器会使用字面常量的大小来代替N和M,从而实例化模板。因此编译器会实例化出如下版本:

int compare(const char (&p1)[3], const char (&p2)[4])//在字面值常量末尾自动添加空字符'\0'

一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针(或左值)引用。绑定到非类型参数的模板实参必须是常量表达式。绑定到指针或引用非类型参数的实参还必须具有静态的生存期。我们不能用一个非static局部变量或动态对象作为指针或引用非类型模板参数的实参,否则编译器无法进行参数推导。

类模板

类模板用来生成类的蓝图。与函数模板的不同之处在于,编译器不能为类模板腿短模板参数类型,我们必须在使用类模板时在模板名后的尖括号中提供额外信息——用来代替模板参数的模板实参列表。

template 
class A {
public:
	A(a) : mem(a) {}
	void print() { std::cout << this->mem << std::endl; }
private:
	T mem;
}

实例化类模板时,必须提供额外信息,如:A A_class 将会生成一个A类型的对象。一个类模板的每个实例都形成一个独立的类,如A和A是两个独立的类。

类模板与友元

当一个类包含一个友元声明时,类与友元各自是否是模板时相互无关的。如果一个类模板包含一个非模板友元,则友元可以被授权访问所有模板的实例,也可以被授权只能访问指定类型的模板实例。如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例。从C++11标准尅是,我们甚至可以将模板类型参数声明为友元。

成员模板

分为两种,一种是非模板类的成员模板,另一种是类模板的成员模板。对于后者,类和成员各自有各自的独立的模板参数,实例化时必须同时提供类和函数模板的实参。

                                                                                        本文部分内容摘自《C++ Primer 中文版(第五版)》

你可能感兴趣的:(c/c++,学习笔记)