c语言中函数名字不可重复,但是可以写代码实现
普通的函数重载
这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同和返回值没有关系(因为就像我想调用Add(1,2),Add重载的几个函数仅仅返回值不同,编辑器就不知道去找哪一个,就有歧义了)
情况1-数组
int ave(int*pa,int count)
和int ave(int pa[],int count);
不可重载
情况2-引用
int ave(int &a,int &b);
和int ave(int a,int b);
编译时候不报错
但是ave(100,200);
运行不报错,因为不可以用常量初始化引用
int a=100,b=200;
ave(a,b); //运行时候报错,因为有歧义,不知道走哪个函数
情况三-强制类型转换
float ave(float a,float b);
int ave(int a,int b);
float a=1.0f,b=2.0f;
ave((int)a,(int)b); //依然是走float,因为强制类型转换后的变量是临时变量,无固定的内存地址,则搞不了引用
情况4-const常量
int ave(int a,int b);
int ave(const int a,const int b);
不可重载,属于函数的const 常量,形同虚设了,编辑器还是会歧义
情况5-const常量引用
int ave(int &a,int &b);
int ave(const int &a,const int &b);
不会歧义,因为第二个函数需要两个const常量来初始化
情况6-函数默认参数
int ave(int a=1,int b=2);
float ave(float a=1.0f,float b=2.0f);
不行,编辑器又歧义了
函数模板(c++专属)
#include<iostream>
using namespace std;
template<typename type1>
type1 ave(type1 a, type1 b)
{
type1 x = a;
return (a + b + x) / 3;
}
int main() {
cout << ave(1, 2) << endl;
cout << ave(1.4, 1.6) << endl;
return 0;
}
也可以搞成指针
type1 *a
c = *ave(&a,&b,&c);
也可以强制指定
cout << ave<float>(1.4, 1) << endl; //参数部分会涉及强制类型转换
址传递的困惑
正常值传递
#include<iostream>
using namespace std;
template<typename type1>
void swap(type1* a, type1* b)
{
type1 temp;
temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 1, b = 3;
swap(&a,&b);
cout << a << " " << b << endl;
return 0;
}
但是这样就失去了模板的意义,就只能搞指针
#include<iostream>
using namespace std;
template<typename type1>
void swap(type1 a, type1 b)
{
type1 temp;
temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 1, b = 3;
swap(&a,&b);
cout << a << " " << b << endl;
return 0;
}
我们很多人会以为是这样写,但是这样的话模板就分不清楚,到底是引用还是指针,而且还报错了
编译器默认成引用了
对于这样有歧义的情况,我们也有对策-可以单独列出例外情况,因为函数重载优先级大于函数模板
但是要是把哪个重载的函数改为void返回值,编译器会报错,要和前面模板一个形式
也可以解决这个问题
所以在处理函数模板例外情况时候,不加template<>
限制会更加灵活
函数模板进阶
带三个参数的函数模板
情况一:就强制指定第一个(一般写返回值),然后将参数类型交给编辑器去猜
#include<iostream>
using namespace std;
template<typename TR,typename T1,typename T2>
TR ave(T1 a, T2 b) {
return (a + b) / 2;
}
int main() {
ave<double>(10, 10.3f);
return 0;
}
情况二:直接在模板函数的形参列表里面强制指定
#include<iostream>
using namespace std;
template<typename TR,typename T1,typename T2>
TR ave(T1 a, T2 b,int c) {
return (a + b) / 2;
}
int main() {
ave<double>(10, 10.3f,30);
return 0;
}
T1之类的就当正常数据类型来用,可以搞指针,可以搞默认参数
decltype和auto的补充知识
#include<iostream>
using namespace std;
template<typename T1, typename T2>
decltype(auto) bigger(T1 &a, T2 &b) { /*decltype默认以return后面的表达式来推断类型,如果不想这样,还是要原始方式结尾*/
return a < b ? a : b;
} //加了decltype就是引用类型了,不加就不是
int main() {
int a = 50;
int b = 50000000;
bigger(b, a) = -250;
cout << b << endl;
return 0;
}
上面代码可以把b改为-250,因为bigger是引用类型
这段代码的反汇编
将a的地址放到eax中,然后后面将-250的补码移到a的位置
去掉decltype这样是报错的,因为就像2500 = -250,右值是不能赋值的
上面这样操作也行,但是因为a,b不是引用所以没有改变全局变量a,b的值,改的是局部变量b的值
对于这个代码的反汇编
先将a的值移到eax中,将-250的补码移到eax寄存器中
一个困惑了很多人的问题(大部分书上没有解决这个问题)
#include<iostream>
using namespace std;
template<typename T1, typename T2>
decltype(auto) bigger(T1 &a, T2 &b) {
return a < b ? a : b;
}
int main() {
float a = 50;
int b = 50000000;
bigger(b, a);
cout << b << endl;
return 0;
}
这个bigger的类型是float还是float&(中间存在强制类型转换,强制类型转换生成的是临时变量),float类型
不能引用强制类型转换的量,因为没有固定内存地址,是右值
改成a都不行