C++函数重载与模板化
概念预备
左值与右值是相对于赋值运算符(=)与累加运算符(+=),以下内容统称为运算符。
左值:存放于运算符左边的值,凡是能取地址(&)都是左值。
右值:存放于运算符右边的值,不能取地址(&)都是右值。
int a = 10, b = 5, c = 3;//a左值,10右值
int a = b + c;//a左值,b+c右值
使用场景
引用必须和变量(也可以说左值)关联起来,如果想引用右值,必须const存放于临时变量,以此进行引用,此为常引用;需要注意因为const的原因此时只读,想要右值可写必须右值引用!
左值引用
int a = 10, b = 5, c = 3;//a左值,10右值
int& c = a;//a左值,c右值
int& b = 10;//错误使用,不可以进行右值引用
int& a = (b + c);//错误使用,不可以进行右值引用
//左值常(量)引用:实际引用的是临时变量
const int& b = 10;//正确
const int& a = (b + c);//正确
右值引用
int a,b,c;
int&& a = 10;
int&& a = ( b + c );
C++提出右值引用并不是为了替代左值引用,而是用于移动语义。右值引用将会为构造函数与析构函数服务,析构函数后续会补充上来。
函数重载与模板化
函数重载常常使用于对不同类型数据做相似操作,当函数命名相同,特征标/形参列表(成员数量,类型,参数顺序)不同时编译器认为函数重载;模板化常用于对不同数据类型做相同操作,常至于类库中。因此使用时时常将函数重载直接模板化。
函数重载:本质是特征标重载
//函数重载
#include <iostream>
using namespace std;
char* left_str(char* str, int length=1);//注意函数重载不能二义性
unsigned long left_str(unsigned long nm, unsigned int ct=1);
int main(void)
{
char arr[40]="xinguanyimiao";
unsigned int year = 50;
left_str(arr,4);//对数组取前三个成员
left_str(1234, 3);//对整形取高三位
return 0;
}
char *left_str(char* str, int length)
{
int i;
if (length > strlen(str) || length < 0)
{
cout << "the is illegal!!" << endl;
return 0;
}
for (i = 0; i < length; i++)
{
cout << str[i];
}
while (length+i <= 40)//填充成员
str[i++] = '\0';
return str;
}
unsigned long left_str(unsigned long nm, unsigned int ct)
{
unsigned long n=nm;
unsigned int i=1;
while (n /= 10)
i++;
if (ct > i)
{
cout << "the length illegal!!" << endl;
return 0;
}
cout << "the par is normal ):" << endl;
i = i - ct;//需要移动的位数
while (i--)
nm /= 10;//除10进行前移
cout << "the n is:" << nm << endl;
return nm;
}
C++一书对函数重载的二义性做了专门的警告:
double show(&a);
double show(a);
int main(void)
{
int a=10;
show(a);//出现二义性
return 0;
}
/*
参数引用与参数本身视为同一种特征标,导致特征标中的参数类型产生二义性
输入的非const参数与函数声明的const参数与非const参数发生冲突时,导致导致特征标中的参数类型产生二义性
函数声明时引入了默认参数,导致特征标中的参数数量或者参数顺序产生二义性
*/
对于函数重载,有一种需要注意的情况就是左值引用和右值引用,如果你给左值加了const,然后输入参数为右值时,编译器会优先调用最匹配的版本,此时并不会产生错误:
在C++一书的举例中,stove(x+y)会优先调用stove(double &&),如果没有定义这个函数,那么因为const的关系将X+Y保存到临时变量中去,此时会调用stove(const doble &r2)这个函数调用临时变量这个左值,但是只读。根据作者的项目经历,在编写测试用例时记得Capl语言中可以进行stove(x+y)传入右值,本质可能是capl编译器对右值和左值做了函数模板化的重载。另外C编译器本质不允许进行函数重载,想要实现类似重载的功能可以借用函数指针等方法,具体可以查阅文章。
模板化:本质是编译器根据传入的参数自定产生函数
template <typename 模板名称>
函数声明//函数(模板名称 参数1,模板名称 参数2)
int main(void)
{
}
函数(模板名称 参数1,模板名称 参数2)
{
//
}
#include <iostream>
using namespace std;
template <typename T>
void swap_per(T& a, T& b);
template <typename T>//函数重载
void swap_per(T a[], T b[], int n);
void show(int arr[], int n);
int main(void)
{
int i = 10, j = 20;
cout << "i, j: " << i << " " << j << endl;
swap_per(i,j);//std类里面有一个swap的函数,因此发生了重载,改变命名
cout << "After swap i, j: " << i << " " << j << endl;
double x = 24.5, y = 81.7;
cout << "x, y: " << x << " " << y << endl;
swap_per(x, y);
cout << "After swap x, y: " << x << " " << y << endl;
int limit[3] = { 9,7,2 };
int limit1[3] = { 0,5,8 };
cout << "Original arrays: ";
show(limit,3);
show(limit1, 3);
swap_per(limit, limit1,3);//函数重载
show(limit, 3);
show(limit1, 3);
return 0;
}
//函数模板--一般会放在头文件里面去用
template <typename T>
void swap_per(T& a, T& b)//函数模板
{
T temp;
temp = a;
a = b;
b = temp;
}
template <typename T>
void swap_per(T a[], T b[],int n)//函数模板重载
{
T temp;
for (int i = 0; i < n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
void show(int arr[], int n)
{
for (int i = 0; i < n; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
可以看出函数模板只能作用于调用形式相同的数据(int,long,double,struct等),想要对调用形式不同的数据类型比如数组与常量做操作必须函数重载的模板化。
函数模板的显示具体化
如上讨论,函数模板化有一个弊端,比如当我们想交换两个结构体得部分参数时,那我们如何传参呢??由此诞生显示具体化,也就是适用于处理特殊参数的函数模板,形式如下:
template <> void swap_per<job>(job& a, job& b)//job是一个结构体定义
{
}
这样就可以当调用到结构体时自动告诉编译器通用的swap_per()函数模板不再有效,避免了函数模板的重载,C++中显示具体化优先级高于函数重载,非模板函数优先级高于模板化函数。
综上关于函数重载,函数模板化,函数模板的重载,函数模板的显示具体化使用总结如下:
- 函数重载:可以解决任何事情,但是数据类型比较多的时候代码量比较大。
- 函数模板化:对通用参数做处理,如long,int,double等
- 函数模板化的重载:对非通用参数也想做处理,如数组
- 函数模板的显示具体化:对特殊参数做处理,如结构体
- 注意:二义性,右值传入必须const另存临时变量或者右值引用
本文来自博客园,作者:{张一默},转载请注明原文链接:https://www.cnblogs.com/YiMo9929/p/16648224.html