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  游翔  阅读(0)  评论(0)    收藏  举报