11-2 函数重载的区分

在上一课(11.1——函数重载介绍)中,我们介绍了函数重载的概念。该机制允许创建多个同名函数,前提是每个同名函数的参数类型不同(或函数可通过其他方式区分)。

本节课我们将深入探讨如何区分重载函数。若重载函数未被正确区分,编译器将报出编译错误。

本节将深入探讨重载函数的区分机制。若重载函数未正确区分,编译器将报错。


重载函数的区分方式

Function property
函数属性
Used for differentiation
区分依据
Notes
备注
Number of parameters
形参数量
Yes
是
Type of parameters
形参类型
Yes
是
Excludes typedefs, type aliases, and const qualifier on value parameters. Includes ellipses.
排除值参数的typedef、类型别名及const限定符。包含省略号。
Return type
返回类型
No
否

注意:函数返回类型不用于区分重载函数,稍后将详细说明。

对于进阶读者

对于成员函数,还会考虑额外的函数级限定符:

Function-level qualifier
函数级限定符
Used for overloading
是否用于区分重载
const or volatile
const 或 volatile
Yes
是
Ref-qualifiers
引用限定符
Yes
是

例如:const成员函数可与形参完全相同的非const成员函数区分(即使它们共享相同形参集)。

相关内容
第20.5课——省略号(及避免使用的原因)将讲解省略号。


基于形参数量的重载

只要每个重载函数的形参数量不同,即可实现区分。例如:

int add(int x, int y)
{
    return x + y;
}

int add(int x, int y, int z)
{
    return x + y + z;
}

编译器可轻松识别:带两个整数形参的调用应指向 add(int, int),带三个整数形参的调用应指向 add(int, int, int)。


基于形参类型的重载

只要每个重载函数的形参类型列表不同,即可实现区分。例如以下所有重载均可区分:

int add(int x, int y); // integer version
double add(double x, double y); // floating point version
double add(int x, double y); // mixed version
double add(double x, int y); // mixed version

由于类型别名(或typedef)并非独立类型,使用类型别名的重载函数无法与使用别名类型本身的重载区分。例如以下所有重载均无法区分(且会导致编译错误):

typedef int Height; // typedef
using Age = int; // type alias

void print(int value);
void print(Age value); // not differentiated from print(int)
void print(Height value); // not differentiated from print(int)

对于按值传递的形参,const限定符同样不予考虑。因此以下函数不被视为可区分:

void print(int);
void print(const int); // not differentiated from print(int)

对于进阶读者

虽然尚未介绍省略号参数,但省略号参数被视为特殊类型的参数:

void foo(int x, int y);
void foo(int x, ...); // differentiated from foo(int, int)

因此调用 foo(4, 5) 将匹配 foo(int, int),而非 foo(int, ...)。


函数返回类型不参与重载区分

区分重载函数时不考虑函数的返回类型。

假设你想编写一个返回随机数的函数,但需要一个返回整数的版本和一个返回双精度浮点数的版本。你可能会尝试这样写:

int getRandomValue();
double getRandomValue();

在 clang21 中,这会导致以下编译器错误:

image

这很合理。如果你是编译器,看到以下语句:

getRandomValue();

你会调用哪个重载函数?这并不明确。

顺带一提...
这是编译器的刻意设计,它确保函数调用的行为可独立于表达式其余部分确定,从而大幅简化复杂表达式的理解。换言之,我们始终能仅凭函数调用的参数确定调用的是哪个版本。若需通过返回值区分,则无法通过语法轻松识别调用的重载版本——还必须理解返回值的使用方式,这需要大量额外分析。

解决此问题的最佳方式是为函数赋予不同的名称:

int getRandomInt();
double getRandomDouble();

类型签名

函数的类型签名type signature(通常称为签名signature)定义为函数头部用于区分函数的组成部分。在C++中,这包括函数名、形参数量、形参类型以及函数级限定符。值得注意的是,它不包含返回类型。


名称修饰

顺带一提……
当编译器编译函数时,它会执行名称修饰name mangling,即根据各种标准(如形参的数量和类型)对函数的编> 译后名称进行更改("修饰"),以便链接器能够使用唯一的名称进行工作。

例如,原型为 int fcn() 的函数可能编译为修饰名 __fcn_v,而 int fcn(int) 则可能编译为修饰名 __fcn_i。因此在源代码中,这两个重载函数共享 fcn() 名称,但在编译后的代码中,修饰名是唯一的(__fcn_v 与 __fcn_i)。

名称修饰方式没有统一标准,因此不同编译器生成的修饰名称各不相同。

posted @ 2026-03-06 15:31  游翔  阅读(27)  评论(0)    收藏  举报