函数重载的区分规则

在上节课(函数重载简介)中,我们引入了“函数重载”这一概念:只要同名函数的参数类型不同(或通过其他方式可区分),即可在同一作用域内定义多个同名函数。本课将深入探讨编译器如何区分这些重载函数;若区分失败,编译器将报错。

如何区分重载函数

函数属性是否参与区分说明
参数个数
参数类型不含 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

不同编译器的改编规则无统一标准,生成的符号名亦不同。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"