在上节课(函数重载简介)中,我们引入了“函数重载”这一概念:只要同名函数的参数类型不同(或通过其他方式可区分),即可在同一作用域内定义多个同名函数。本课将深入探讨编译器如何区分这些重载函数;若区分失败,编译器将报错。
如何区分重载函数
函数属性 | 是否参与区分 | 说明 |
---|---|---|
参数个数 | 是 | — |
参数类型 | 是 | 不含 typedef、类型别名以及按值传递时的顶层 const;包含可变参数。 |
返回类型 | 否 | — |
注意:返回类型不参与重载区分,稍后详述。
供进阶读者 对于成员函数,以下附加限定符也参与区分:
const
/volatile
限定符- 引用限定符(ref-qualifiers)
例如,一个
const
成员函数可与同参数列表的非const
成员函数并存。
相关内容 可变参数(ellipsis)将在 20.5 课——可变参数及其避免方法中讨论。
按参数个数区分
只要同名函数的参数个数不同,即可区分:
int add(int x, int y)
{
return x + y;
}
int add(int x, int y, int z)
{
return x + y + z;
}
编译器可根据实参个数 2 或 3 选择对应版本。
按参数类型区分
同名函数的参数类型列表必须互不相同。示例:
int add(int x, int y); // 整型版
double add(double x, double y); // 浮点版
double add(int x, double y); // 混合型
double add(double x, int y); // 混合型
需注意:
- 类型别名 / typedef 不视为新类型,因此下列重载不区分:
using Age = int; typedef int Height; void print(int value); void print(Age value); // 与 print(int) 冲突 void print(Height value); // 与 print(int) 冲突
- 按值传递时的顶层
const
不区分:void print(int); void print(const int); // 与上面冲突
供进阶读者 可变参数被视为一种特殊参数类型,可与固定参数列表区分:
void foo(int x, int y);
void foo(int x, ...); // 与 foo(int,int) 区分
返回类型不参与区分
返回类型不能用于区分重载函数。 若试图写出:
int getRandomValue();
double getRandomValue(); // 只凭返回类型不同
Visual Studio 2019 报错:
error C2556: 'double getRandomValue(void)': overloaded function differs only by return type
原因显而易见:
getRandomValue(); // 编译器无法确定调用哪个版本
因此,应改用不同函数名:
int getRandomInt();
double getRandomDouble();
旁注 此设计可避免“根据返回值使用方式”才能确定调用版本的情形,简化复杂表达式的理解。
类型签名(Type Signature)
函数的类型签名(type signature)指用于区分该函数的头文件部分:函数名、参数个数、参数类型以及成员函数的限定符。返回类型不包含在内。
名字改编(Name Mangling)
编译器在生成目标代码时会对函数名进行名字改编(name mangling),根据参数个数、类型等信息生成唯一符号名。例如:
int fcn()
可能被改编为__fcn_v
int fcn(int)
可能被改编为__fcn_i
不同编译器的改编规则无统一标准,生成的符号名亦不同。