默认参数必须从右向左设置,只能在声明中定义且不可依赖未默认化参数;重载与默认参数不可混用,因默认参数不参与重载决议。

默认参数必须从右向左设置,否则编译直接报错
因为 C++ 的函数调用语法不支持“跳过中间参数”。当你写 func(1, , 3) 这种形式是非法的——C++ 没有类似 Python 的关键字参数(func(a=1, c=3))机制,所有实参严格按位置从左到右匹配形参。如果允许 int func(int a = 1, int b, int c = 3),那么调用 func(2, 5) 时,编译器无法判断你是想设 a=2, b=5(c 用默认值),还是 b=2, c=5(a 用默认值)——歧义不可解,所以语言层面禁止。
声明和定义分离时,默认参数只能出现在声明中
这是为避免二义性和 ODR(One Definition Rule)违规。如果你在头文件里声明了 void log(const char* msg, int level = 1);,又在 .cpp 文件定义里写了 void log(const char* msg, int level = 1) { ... },某些编译器会警告或报错;更糟的是,若头文件被多个源文件包含,而不同地方看到的默认值不一致(比如宏导致值变化),链接期行为就不可控。
- 只在头文件的函数声明中写默认值,.cpp 中定义时省略等号和值
- 如果函数只有定义、没有单独声明(比如 static 函数或仅在单个 .cpp 内使用),那默认值可以且只能写在定义处
- 不能在声明里写一部分、定义里补另一部分——默认参数必须完整、唯一、一次性给出
默认参数值不能依赖左侧未默认化的参数
像 void foo(int a, int b = a + 1) 是非法的。因为默认值表达式在编译期求值(或至少在函数声明可见时就需确定),而 a 是运行时才传入的变量,不是常量表达式。能用的默认值限定为:
- 字面量:
int x = 42 - 全局常量或 constexpr 变量:
constexpr int kDefault = 10;→int y = kDefault - 函数名(代表地址,是编译期常量):
void handler() {...}; void run(std::function<void> f = handler)</void>合法,但注意这会隐式构造 std::function 对象 - 不能是局部变量、非 constexpr 静态变量、this 指针、或任何含运行时依赖的表达式
重载与默认参数混用容易引发意外匹配
默认参数不参与函数类型构成,也不用于重载决议。例如:
void f(int x); void f(int x, int y = 0);
这**不是重载**,而是重复定义,多数编译器会报错。真正合法的重载必须靠参数类型或数量区分,比如:
void f(int x)void f(double x)void f(int x, int y)
如果你本意是“提供便捷调用”,用默认参数;如果本意是“处理不同类型输入”,用重载。两者目的不同,强行混用会让调用者困惑,也让编译器难以推导最佳匹配。
默认参数看着简单,但它的限制都来自 C++ 对“调用无歧义”和“编译期可确定”的刚性要求。最容易被忽略的一点是:它和重载根本不是一回事,也替代不了重载——该用重载的地方硬塞默认参数,后期维护时第一个踩坑的就是你自己。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











