函数重载与重载解析
函数重载
函数重载是C++引入的,C语言没有。
int fun(int x)
{
return x + 1;
}
int main()
{
cout << fun(3) << endl; // 4
cout << fun(3.5) << endl; // 4 warning: implicit conversion from 'double' to 'int' changes value from 3.5 to 3 [-Wliteral-conversion]
}
函数重载(参数类型不同)
int fun(int x)
{
return x + 1;
}
double fun(double x)
{
return x + 1;
}
int main()
{
cout << fun(3) << endl; // 4
cout << fun(3.5) << endl; // 4.5 函数重载 调用double fun(double x)
}
函数重载(参数个数不同)
int fun(int x)
{
return x + 1;
}
int fun(int x, int y)
{
return x + y;
}
int main()
{
cout << fun(3) << endl; // 4
cout << fun(3, 4) << endl; // 7 函数重载 调用 int fun(int x, int y)
}
不能急于不同函数返回类型进行函数重载
上述情况是重定义
函数重载与name mangling
编译器如何选择正确的函数版本来进行调用:重载解析
名称查找 name lookup
给一个函数,系统要知道调用哪个函数去。
限定的名称查找,对查找的区域进行了限定,编译器会根据限定去查找
void fun()
{
cout << "global fun is called" << endl;
}
namespace MyNS
{
void fun()
{
cout << "MyNS fun is called" << endl;
}
}
int main()
{
// 限定的名称查找
::fun(); // 全局的fun函数调用
MyNS::fun(); // namespace MyNS中的fun被调用
}
非限定的名称查找
void fun()
{
cout << "global fun is called" << endl;
}
namespace MyNS
{
void fun()
{
cout << "MyNS fun is called" << endl;
}
}
int main()
{
fun(); // global fun is called 非限定名称查找,调用了全局的
}
非限定的名称查找会进行域的逐级查找,造成名称的隐藏。
void fun()
{
cout << "global fun is called" << endl;
}
namespace MyNS
{
void fun()
{
cout << "MyNS fun is called" << endl;
}
void g()
{
fun(); // 这里会调用哪个fun呢?
}
}
int main()
{
MyNS::g(); // "MyNS fun is called" 调用了MyNS中的fun函数。域的逐级查找
// g属于MyNS,系统会先在MyNS中查找有没有fun函数。
}
void fun()
{
cout << "global fun is called" << endl;
}
namespace MyNS
{
void g()
{
fun(); // 这里会调用哪个fun呢?
}
}
int main()
{
MyNS::g(); // "global fun is called" 调用了全局中的fun函数。因为MyNS中没有fun了
}
实参依赖查找:ADL(Argument Dependent Lookup)
注意,ADL只对自定义的类型有效。内建类型无效。
重载解析
过滤不能被调用的版本。
#include <iostream>
#include <initializer_list>
#include <string>
using namespace std;
void fun(int x)
{
}
void fun(std::string x)
{
}
int main()
{
fun(3); // 只会调用第1个fun,第2个被过滤掉
}
过滤不能被调用的版本后,int和double的fun都被保留了。
在剩余的表达式,匹配最匹配的fun函数。
最后3匹配int最好。
#include <iostream>
#include <initializer_list>
#include <string>
using namespace std;
void fun(int x)
{
}
void fun(double x)
{
}
int main()
{
fun(3);
}
promotion: short到int,int到long
标准转换:int到double。
省略号…:可以匹配任何参数,匹配级别最高,也就是最cheap的匹配。
void fun(int x)
{
cout << 1;
}
void fun(double x) // 级别3,标准匹配
{
cout << 2;
}
int main()
{
fun(3); // 匹配int x,完美匹配
}
内联函数 constexpr函数
递归,就是函数调用自己。
循环是一种特殊的迭代。迭代是循环的扩展。
内联函数是一种优化机制。编译器会选择展开还是不展开,我们不用管。
展开的原因是关联的函数比较简单,调用要开栈,销毁等,比较消耗效率,所以就展开了。但是有的函数逻辑比较复杂,编译器不选择展开。
展开不是简单的替换。避免名字的冲突。
如果程序中,只看到了函数的声明,而没有看到函数的定义,是无法进行展开的。
c++标准定义,如果一个函数出现了inline,多个函数中,连接器就选择一个。
constexpr是一个编译器常量,在编译期就可以确定的常量。
constexpr int fun()
{
return 3;
}
constexpr int x = fun(); // 因为fun在编译期可以求值,所以在编译期确定x
int main()
{
}
一般的函数,可以在运行期求值,函数体里可以包含一系列可以work的代码。但是如果有变量需要在运行期求值,那么就不能用constexpr。
consteval函数:函数只能在编译期求值。C++20.
为什么要引入constexpr,有一些函数写出来,目的就是告诉系统在编译期执行出来。执行效率提升。
编译器在处理时,是以cpp文件为单元进行处理的。为什么能在编译期处理程序语句呢?所以constexpr必须是在同一个cpp文件内,而不是在别的翻译单元。这有点像内敛函数,进行展开。
函数指针
什么是函数的指针类型?
int inc(int x)
{
return x + 1;
}
int dec(int x)
{
return x - 1;
}
using K = int(int);
int main()
{
K* fun = &inc; // fun是一个指针,指向了inc这个对象
cout << (*fun)(100) << endl; // 101 *fun解引用
}
我们为什么要引入函数指针这个概念?
作为高阶函数来使用,一个函数可能接收另外一个函数,或者返回另外一个函数。这样的函数是一个高阶函数。
int inc(int x)
{
return x + 1;
}
int dec(int x)
{
return x - 1;
}
using K = int(int);
int Twice(K* fun, int x) // 高阶函数,接受一个函数指针
{
int tmp = (*fun)(x);
return tmp * 2;
}
int main()
{
cout << Twice(&inc, 100) << endl; // 202 &inc的形参是一个inc函数指针
cout << Twice(&dec, 100) << endl; // 传入不同的函数
}
#include <iostream>
#include <initializer_list>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int myinc(int x)
{
return x + 1;
}
int mydec(int x)
{
return x - 1;
}
using K = int(int);
int main()
{
std::vector<int> a = {1, 2, 3, 4, 5};
std::transform(a.begin(), a.end(), a.begin(), &myinc); // transform是一个泛型函数
for (int i = 0; i < 5; ++i)
{
cout << a[i] << endl; // 23456
}
std::transform(a.begin(), a.end(), a.begin(), &mydec);
for (int i = 0; i < 5; ++i)
{
cout << a[i] << endl; // 01234
}
}
函数是不能复制的,就像数组无法复制一样。
如果把一个对象付给一个函数,这个对象会自动转化成函数指针。
因为函数不能复制,所以函数返回也是一个函数指针。
函数指针都是与高阶函数配合使用的。
int myinc(int x)
{
return x + 1;
}
int mydec(int x)
{
return x - 1;
}
auto fun(bool input)
{
if (input)
return myinc;
else
return mydec;
}
int main()
{
cout << (*fun(true))(100) << endl; // 证明返回的是函数指针
}
但在C++有很多函数指针的代替品,比如lambada表达式。