C++基础:函数重载
在编写函数时,针对于同一行为,我们希望能够使用相同的名字,而不是再又重新命名。这样做的好处就是能够减少同样行为函数的命名记忆,降低复杂性。可是这会引入另一个问题,这不会违反单一定义原则吗?C++中的重载机制帮我们实现了这一点,下面就看一下它是怎么区分这些函数的,那些又是它不能区分的?
1、参数的数量不同 √
#include <iostream>
int add(int x, int y)
{
return x + y;
}
int add(int x, int y, int z)
{
return x + y + z;
}
int main()
{
std::cout << add(1, 2) << '\n';
std::cout << add(1, 2, 3) << '\n';
return 0;
}

#include <iostream>
typedef int INT;
using INT2 = int;
int add(INT x, INT2 y) // int add(int x, int y)
{
return x + y;
}
int main()
{
std::cout << add(1, 2) << '\n';
return 0;
}
2、参数的类型不同 √
#include <iostream>
int add(int x, int y)
{
return x + y;
}
double add(double x, double y)
{
return x + y;
}
int main()
{
std::cout << add(1, 2);
std::cout << '\n';
std::cout << add(1.2, 3.4);
return 0;
}

像下面的混合形参都能区分
int add(int x, double y);
int add(double x, int y);
3、函数返回值不同 x
#include <iostream>
int add(int x)
{
std::cout << x << '\n';
return 0;
}
double add(int x)
{
std::cout << x << '\n';
return 0;
}
int main()
{
add(5);
return 0;
}

总结:编译器如何区分函数重载
| 函数属性 | 是否区分 |
|---|---|
| 参数数量 | 是 |
| 参数类型 | 是 |
| 返回类型 | 否 |
注意:对于不能用返回类型来区分函数重载,这是故意的,因为这涉及到函数返回的类型的用法,给分析重载带来不小的负担,而直接明了的根据函数参数数量和类型要轻松的多。
对于重载函数,可能有多个函数可能匹配函数调用。由于函数调用只能解析为其中一个,因此编译器必须确定哪个重载函数是最佳匹配。将函数调用与特定的重载函数匹配的过程称为重载解析。
#include <iostream>
void print(int x)
{
std::cout << x << '\n';
}
void print(double d)
{
std::cout << d << '\n';
}
int main()
{
print('a');// char does not match int or double, so what happens?
return 0;
}
当对重载函数进行函数调用时,编译器会逐步执行一系列规则来确定哪个重载函数(如果有)是最佳匹配。
在每个步骤中,编译器都会对函数调用中的参数应用一系列不同的类型转换。对于每个应用的转换,编译器都会检查是否有任何重载函数匹配。在应用所有不同的类型转换并检查匹配后,该步骤就完成了。结果可能是以下三种结果之一:
- 未找到匹配的函数。编译器将按序列转到下一步。
- 找到了一个匹配函数。该函数被认为是最佳匹配。匹配过程现已完成,后续步骤不再执行。
- 找到了多个匹配的函数。编译器会抛出一个模糊匹配编译错误。
因为函数print(‘a’)调用的a是一个char类型,它匹配到了print(int ) 和print(double)两个函数,而char是以ascill码存在的,其char->int属于是整型提升,代价相比于char -> double这种整数和浮点数之间的转换,它会选择转换代价更小的char->int整型提升。
补充编译器在找最佳匹配时会分为两个阶段,第一判断是否位重载,第二会做些简单的转换(也被称为精准匹配,它们会修改类型,但不修改值)。这些简单的转换所遵循的规则:
- 左值到右值的转换
- 限定转换(例如,非常量到常量)
- 非引用到引用转换
或许对于类型提升优先于类型转换这种还比较好理解型,但是对于long 到int, 或者是long 到 double,这种呢?long到int有可能引起窄化转化(做不到值保护的类型转换),而long到double这种不同类型转化代价会更大(整型和浮点型在存储中的方式是完全不一样的),像这样不能平衡他们优先级的,或者说比较模糊的会怎么转换呢?
1 #include <iostream>
2 void foo(unsigned int)
3 {
4 }
5
6 void foo(float)
7 {
8 }
9
10 int main()
11 {
12 foo(0); // int can be numerically converted to unsigned int or to float
13 foo(3.14159); // double can be numerically converted to unsigned int or to float
14
15 return 0;
16 }
(代码复制中带有编译器中的行数,所以要删除掉才能运行)
尽管我们可能期望0解析为foo(unsigned int)并3.14159解析为foo(float),但这两个调用都会导致模糊匹配,当编译器不知道那个匹配得更好时,它就会报错。
解决模糊匹配有两种方法:
- 第一种定义一个新的重载函数,这是最简单的方法。
- 第二种是使用强制类型转换。比如static_cast<dest_type>(source_type)
更多推荐



所有评论(0)