cpp
#include <iostream>
using namespace std;
//一般的函数的使用
void func0(int a)
{
cout << "this is func int a" << endl;
}
//函数占位符语法的使用
void func1(int a, int)
{
cout << "this is func int" << endl;
}
void func2(int a, int = 10)//函数占位符设置默认参数
{
cout << "this is func int = 10" << endl;
}
int main()
{
int a = 10;
func0(10);
func1(10,10);
func2(10, 10);
return 0;
}

这段代码演示了C++中函数占位参数(placeholder parameter)的基本用法。占位参数是指在函数定义中只指定类型而不指定参数名的参数,这种参数在函数体内无法直接使用,但它仍然参与函数的参数列表,影响函数的签名和重载决议。
下面逐一对代码中的函数进行讲解:
1. 普通函数 --- 无占位参数
cpp
void func0(int a)
{
cout << "this is func int a" << endl;
}
这是最常见的函数形式,有一个明确的参数 a,函数体内可以通过 a 访问传入的值。调用时只需提供一个实参:func0(10);。
2. 带占位参数的函数
cpp
void func1(int a, int) // 第二个参数只有类型,没有名字
{
cout << "this is func int" << endl;
}
这里第二个参数 int 就是占位参数。它没有参数名,因此在函数体内无法引用它。但是调用该函数时必须提供两个实参,例如 func1(10, 10);。即使第二个参数的值在函数内部用不到,也必须传一个整数。这种语法通常用于:
-
运算符重载 (如后置
++需要一个int占位参数来与前置++区分)。 -
未来扩展:预留参数位置,后续修改函数时可以不改变调用代码。
-
函数重载:通过参数个数或类型区分重载版本。
3. 带默认值的占位参数
cpp
void func2(int a, int = 10) // 占位参数带有默认值
{
cout << "this is func int = 10" << endl;
}
占位参数也可以赋予默认参数值。这里第二个参数虽然是占位符,但默认值为 10。因此调用时可以选择只传一个参数(第二个参数使用默认值),也可以传两个参数(第二个参数覆盖默认值)。例如:
cpp
func2(10); // 第二个参数使用默认值 10
func2(10, 20); // 第二个参数显式传入 20
在函数体内仍然无法使用第二个参数的值,但默认值机制保证了调用灵活性。
注意事项
-
占位参数在函数体内不能访问,因为它没有名字。如果试图使用它,编译器会报错。
-
如果占位参数没有默认值,调用时必须提供对应实参;如果有默认值,则可以省略。
-
占位参数常用于运算符重载(特别是后置递增/递减)、函数重载的区分,以及为未来库升级预留接口。
-
虽然占位参数看起来"无用",但它参与函数的类型,因此可以影响函数重载的选择。
cpp
//函数重载
#include <iostream>
using namespace std;
//1.讲解1:函数重载体现在函数参数的个数的问题
void func()
{
cout << "this is func()" << endl;
}
void func(int a)
{
cout << "this is func(int a)" << endl;
}
int main()
{
int a = 10;
func();
func(10);
return 0;
}

这段代码演示了 C++ 中函数重载的一种常见形式------通过参数的个数来区分同名函数。
-
在全局作用域中定义了两个同名的
func函数:-
第一个
func()没有参数; -
第二个
func(int a)有一个整型参数。
-
-
虽然函数名相同,但它们的参数列表不同(一个是0个参数,一个是1个参数),因此构成了函数重载。
-
在
main函数中分别调用func()和func(10):- 编译器会根据调用时提供的实参个数自动匹配对应的函数版本。无参调用匹配第一个,带一个整型参数的调用匹配第二个。
-
运行结果会分别输出:
text
this is func() this is func(int a)
函数重载的好处是可以用同一个函数名表达相似的操作(比如打印不同形式的信息),而由编译器根据实参决定具体调用哪一个,使代码更简洁、易读。需要注意的是,重载必须依靠参数列表的差异(参数个数、类型或顺序)来实现,返回值类型不能作为重载的依据。
cpp
#include <iostream>
using namespace std;
//2.讲解2:函数重载体现在函数参数的参数类型的问题
void func(double a)
{
cout << "this is func(double a)" << endl;
}
void func(int a)
{
cout << "this is func(int a)" << endl;
}
int main()
{
int a = 10;
func(3.14);
func(10);
return 0;
}

这是一段演示 C++ 函数重载(Function Overloading)的典型代码,重点展示如何通过参数类型的不同来区分同名函数。
-
函数重载的定义
在同一个作用域内,可以定义多个同名函数,只要它们的参数列表 (参数个数、类型或顺序)不同即可。编译器会根据调用时传入的实参类型自动选择匹配的版本。
-
本例中的重载依据
两个
func函数:-
func(double a)接受double类型参数。 -
func(int a)接受int类型参数。它们的参数类型不同,因此构成重载。
-
-
调用时的匹配过程
-
func(3.14):字面量3.14默认是double类型,所以精确匹配到func(double),输出this is func(double a)。 -
func(10):字面量10是int类型,精确匹配到func(int),输出this is func(int a)。
-
-
注意事项(可选扩展)
-
如果传入一个
float字面量(如3.14f),编译器会优先尝试精确匹配,但这里没有func(float),因此会通过标准转换匹配func(double)(因为float可转换为double),而不会匹配func(int)(因为浮点转整型是降级转换,优先级较低)。 -
重载决策还会考虑隐式类型转换、默认参数等因素,但本例中只涉及精确匹配,简单明了。
-
cpp
#include <iostream>
using namespace std;
//3.讲解3:函数重载体现在函数参数的顺序的问题
void func(double a,int b)
{
cout << "func(double a,int b)" << endl;
}
void func(int a,double b)
{
cout << "func(int a,double b)" << endl;
}
int main()
{
int a = 10;
func(3.14,10);
func(10,3.14);
return 0;
}

-
函数重载的依据
函数重载要求同名函数的参数列表 不同。参数列表的差异不仅包括参数个数、参数类型,还包括参数的顺序------只要参数类型的顺序不同,就可以构成重载。
-
本例中的重载实现
两个
func函数:-
func(double a, int b):第一个参数是double,第二个是int。 -
func(int a, double b):第一个参数是int,第二个是double。虽然两个函数都接收一个
double和一个int,但顺序不同,因此它们是不同的函数签名,构成重载。
-
-
调用时的匹配过程
-
func(3.14, 10):实参依次为double(3.14)和int(10),与第一个版本func(double, int)完全匹配,输出func(double a, int b)。 -
func(10, 3.14):实参依次为int(10)和double(3.14),与第二个版本func(int, double)完全匹配,输出func(int a, double b)。
-
-
为什么顺序重要?
在 C++ 中,函数签名由函数名和参数类型列表(包括顺序)唯一确定。即使参数类型集合相同,只要排列顺序不同,编译器就能区分它们。这为设计提供了灵活性,比如处理不同类型数据但操作逻辑不同的场景。
-
注意事项(可选扩展)
-
如果调用时传入的类型顺序不严格匹配(例如
func(3.14f, 10),第一个实参是float),编译器会尝试隐式类型转换来决定调用哪个版本。但本例中由于精确匹配存在,不会触发转换。 -
必须确保至少有一个参数类型顺序不同,否则会导致重定义错误。例如如果两个函数都是
func(int, double),就会编译失败。
-
函数的返回类型不能作为重载的决定条件。下面我们来详细解释一下这段代码和它产生的错误。
cpp
#include <iostream>
using namespace std;
//4.函数返回参数的类型是不可以作为函数重载的条件的
void func(double a, int b)
{
cout << "func(double a,int b)" << endl;
}
int func(double a, int b)//error!
{
cout << "func(int a,double b)" << endl;
}
int main()
{
int a = 10;
func(3.14, 10);
return 0;
}

在C++中,函数重载(overloading)允许我们定义多个同名函数,只要它们的参数列表不同(参数个数、类型或顺序不同)。这样编译器就能根据调用时传入的实参类型,决定应该调用哪一个函数。
但是,返回类型并不参与重载的区分 。也就是说,不能仅仅通过改变返回类型来重载一个函数。因为当调用一个函数时,编译器需要根据参数来决定调用哪个版本,而返回类型只有在函数调用完成后才发挥作用,并且有时调用者可能忽略返回值(例如直接调用func(...)),这时返回类型就无法提供足够的信息来消除歧义。
错误分析
你的代码中定义了两个func函数:
-
第一个:
void func(double a, int b) -
第二个:
int func(double a, int b)
它们的参数列表完全一样 :都是(double, int)。尽管返回类型不同(一个是void,一个是int),但编译器在重载解析时只看参数,因此会认为这是对同一个函数的重复定义,从而引发编译错误。
错误信息通常类似于:
text
error: functions that differ only in their return type cannot be overloaded
正确的做法
如果你希望用返回类型来区分函数,那是不允许的。你应该让参数列表有所不同。例如:
cpp
void func(double a, int b) { ... }
int func(int a, double b) { ... } // 参数类型顺序不同,构成重载
或者改变参数个数、类型等。这样编译器就能根据调用时提供的实参,正确匹配对应的函数。
关于你的main函数
main中调用func(3.14, 10),实参是double和int,本意可能是想匹配第一个函数。但由于第二个非法定义的存在,编译本身就无法通过。即使第二个定义合法(比如参数顺序互换),这个调用也会匹配参数最匹配的那个版本,例如void func(double, int)会优先于int func(int, double),因为实参类型完全吻合。
小结
-
函数重载的依据是参数列表(参数个数、类型、顺序),返回类型不参与。
-
两个函数如果参数列表完全相同,仅返回类型不同,则不能重载,会导致编译错误。
-
在设计重载函数时,务必确保每个版本的参数列表有足够差异,以便编译器能够唯一确定调用哪个版本。
cpp
//函数重载需要注意的两个点
#include <iostream>
//1.函数重载遇到引用参数
using namespace std;
void func(int& a)
{
cout << "void func(int& a)" << endl;
}
void func(const int& a)
{
cout << "void func(const int& a)" << endl;
}
int main()
{
int a = 10;
func(a);
func(10);
return 0;
}

关键点解析
-
非常量引用
int&-
只能绑定到左值 (例如变量
a),不能绑定到字面量(如10)或临时对象。 -
在
func(a)中,a是一个左值,且类型匹配int&,因此编译器选择void func(int& a)。
-
-
常量引用
const int&-
可以绑定到左值或右值(字面量、表达式结果等),因为常量引用不会修改原值,所以允许临时对象绑定到它。
-
func(10)传递的是字面量(右值),无法绑定到int&,但可以绑定到const int&,因此调用第二个版本。
-
-
重载决议规则
-
当两个函数都可行时,编译器会选择最匹配的版本。对于左值,非常量引用优于常量引用(因为更精确);对于右值,只有常量引用可行。
-
如果没有定义
const int&版本,func(10)会导致编译错误(无法将右值绑定到int&)。
-
为什么要这样设计?
-
非常量引用暗示函数可能修改实参,因此不能接受常量对象或字面量,保证安全性。
-
常量引用表示"只读"操作,可以接受更广泛的实参类型,提高了灵活性。
在编写重载函数时,务必注意引用参数的 const 属性,否则可能会导致意外的匹配结果或编译失败。这个例子很好地展示了引用与 const 在重载中的微妙交互。
cpp
#include <iostream>
//2.函数重载遇到默认参数
using namespace std;
void func(int a,int b=20)
{
cout << "void func(int a,int b=20)" << endl;
}
void func(int a)
{
cout << "void func(int a)" << endl;
}
int main()
{
int a = 10;
func(10);
return 0;
}

函数重载规则
C++允许在同一作用域内定义多个同名函数,只要它们的参数列表不同(参数个数、类型或顺序不同)。编译器根据调用时传入的实参去匹配最合适的函数。
默认参数的作用
函数func(int a, int b = 20)声明了第二个参数默认值为20。这意味着调用时可以只传一个实参,缺失的第二个参数会自动补为20,因此该函数也可以用单个实参调用。
问题出现
在main中执行func(10)时,编译器面临两个候选:
候选1:func(int a, int b = 20) ------ 只需传1个参数即可调用。
候选2:func(int a) ------ 正好需要1个参数。
两个函数都能匹配本次调用,且匹配程度相同(都是精确匹配),因此编译器无法决定调用哪一个,从而产生二义性错误
cpp
#include <iostream>
//2.函数重载遇到默认参数
using namespace std;
void func(int a, int b = 20)
{
cout << "void func(int a,int b=20)" << endl;
}
void func(int a)
{
cout << "void func(int a)" << endl;
}
int main()
{
int a = 10;
func(10,30);
return 0;
}

-
函数重载 :这里定义了两个同名的
func函数,参数列表不同------一个接收两个int(第二个有默认值),另一个只接收一个int。这是合法的重载。 -
默认参数的作用 :第一个函数为第二个参数提供了默认值
20,意味着它可以被以 1 个或 2 个实参的方式调用:-
func(10)→ 等价于func(10, 20) -
func(10, 30)→ 显式传递两个参数,默认值被覆盖
-
-
调用的选择 :在
main中,我们使用func(10, 30)明确传入了两个实参。编译器进行重载解析时,会寻找参数个数匹配且类型兼容的函数。此时:-
第一个函数(
void func(int, int))完美匹配两个int参数。 -
第二个函数(
void func(int))只接受一个参数,不匹配两个实参。 -
因此,编译器毫无歧义地选择第一个函数,输出:
void func(int a,int b=20)
-
-
潜在的二义性 :如果调用改为
func(10),情况就不同了:-
第一个函数可以通过默认参数匹配(
func(10)→func(10, 20))。 -
第二个函数直接匹配(
func(10))。 -
两个函数在重载解析中优先级相同(都是精确匹配),导致编译器报错 "对重载函数的调用不明确"(ambiguous call)。
-
结论
-
当函数重载遇上默认参数时,设计需格外谨慎。尤其是那些可以"通过默认参数补全"的调用,容易与参数较少的重载版本产生冲突。
-
本例中的
func(10, 30)调用是明确的,因为实参个数直接区分了重载版本;但若调用时省略第二个参数,就会引发二义性。