问题一:函数模板有什么用?
所谓模板,实际上是建立一个通用函数或类,其类内部的类型和函数的形参类型不具体指定,用一个虚拟的类型来代表。这种通用的方式称为模板。模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
这是C++提供的模板(template)编程的概念。从中大致可以看出模板可实现独立于任何特定类型的方式编写代码。然后我们通过一个例子去感受下函数模板的作用。
问题是交换两个元素并返回,元素有各种类型,如char、int、double等,如果使用普通函数那么就可能要针对每种类型写一个函数。函数模板代码如下:
template<typename T> //我的模板叫T
void myswap(T &a, T &b) {//T和其他的类一样的使用
cout << "我是函数模板" << endl;
T c = a;
a = b;
b = c;
}
然后我们在主函数中调用我们的函数模板:
void main() {
int aa = 10, bb = 20;
double a = 10.11, b = 20.22;
myswap(aa, bb);
myswap(a, b);
cout << "交换后" << endl;
cout << "aa=" << a << " bb=" << b << endl;
cout << "a=" << a << " b=" << b << endl;
}
结果如下:
可以看出一个函数模板实现了对两种不同类型的数据实现了交换。
问题二:函数模板生成函数实例的过程
前面我们说函数模板可以当抽象类使用,因此也可以进行实例化,那么其实例化过程是怎样的?
在程序运行过程中,模板函数会进行两次编译,第一次编译函数模板,第二次编译是根据泛型具体的类型进行编译并进行实例化,我们结合反汇编可以更深入的理解。
可以看出函数模板根据具体的类型实例化出了对应的函数
问题三:普通函数与模板函数重载
1.可以重载吗?(可以)
2.重载了优先调用哪个函数呢?(普通函数)
3.当有对应的普通函数是我可以硬用函数模板吗?(可以)
下面我们通过具体的例子对这几个问题进行回答。
template<typename T> //我的模板叫T
void myswap(T &a, T &b) {
cout << "我是函数模板" << endl;
T c = a;
a = b;
b = c;
}
void myswap(int &a, int &b) {
cout << "我是普通函数" << endl;
int c = a;
a = b;
b = c;
}
上面声明了一个普通函数和一个函数模板,下面对这两个函数进行调用
void main() {
int aa = 10, bb = 20;
double a = 10.11, b = 20.22;
cout << "aa=" << a << " bb=" << b << endl;
cout << "a=" << a << " b=" << b << endl;
myswap(aa, bb);
myswap(a, b);
cout << "交换后" << endl;
cout << "aa=" << a << " bb=" << b << endl;
cout << "a=" << a << " b=" << b << endl;
}
由此可以得出
1. 可以重载
2. 当有对应的普通函数时,相比函数模板,程序优先调用普通函数。
那么我对应的普通函数是我可以硬用函数模板吗?可以的,函数模板的实例化有隐性实例化和显式实例化。函数模板隐式实例化指的是在发生函数调用的时候,如果没有发现相匹配的函数存在,编译器就会寻找同名函数模板,如果可以成功进行参数类型推演,就对函数模板进行实例化。对于函数模板而言,不管是否发生函数调用,都可以通过显示实例化声明将函数模板实例化。
下面我们通过一个例子来说明如何进行显式实例化
void main() {
int aa = 10, bb = 20;
double a = 10.11, b = 20.22;
cout << "aa=" << a << " bb=" << b << endl;
cout << "a=" << a << " b=" << b << endl;
myswap<int>(aa, bb);
myswap(a, b);
cout << "交换后" << endl;
cout << "aa=" << a << " bb=" << b << endl;
cout << "a=" << a << " b=" << b << endl;
}
可以看出,就算有匹配的函数时,我们依然可以通过显式实例化调用函数模板。
问题四:出现不符合函数模板实现方式的特例怎么做? 特化
模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本。
假如我们要交换两个有两个元素的元组的第二个元素,显然,上面的模板函数就不适用了,就要对这类函数进行特化。
template < >
void myswap(pair<int,int> &a, pair<int, int> &b) {
int c = a.second;
a.second = b.second;
b.second = c;
}
void main() {
pair<int, int> a = { 1,2 }, b = {3,3};
cout <<"a: "<<a.first << " " << a.second << endl;
cout << "b: " << b.first << " " << b.second << endl;
myswap(a, b);
cout << "交换后" << endl;
cout << "a: " << a.first << " " << a.second << endl;
cout << "b: " << b.first << " " << b.second << endl;
}
显然,完成了交换。
上面特化模板函数的声明所用的语句template < >表明这个函数是函数模板的特化。
另外有一个问题,可以同时声明template < >void myswap(int &a, int &b) 和void myswap(int &a, int &b)这样的两个函数吗?答案是可以的,第一个函数是函数模板的一部分,而第二个是普通函数,当有匹配的函数时,如果没有硬用函数模板,依然优先调用普通函数。