函数重载仅依据参数列表(个数、类型、cv限定符)区分,返回类型无关;void f(int)与void f(const int)不构成重载,但void f(int&)与void f(const int&)合法;隐式转换歧义、模板优先级、const/引用限定符缺失是常见陷阱。

函数重载必须靠参数列表区分,返回类型不算
函数重载不是“同名就行”,C++ 只看参数个数、类型、cv 限定符(const/volatile)是否不同,return 类型完全无关。写重载时如果只改了返回值,编译器会报 redefinition 错误。
- 合法:一个
void print(int)和一个void print(double) - 非法:一个
int func()和一个double func()—— 编译失败 - 注意:
void f(int)和void f(const int)不构成重载(顶层 const 被忽略) - 但
void f(int&)和void f(const int&)是合法重载(引用绑定行为不同)
重载解析失败常见于隐式转换链过长
当你调用 add(1, 2.5),而只有 add(int, int) 和 add(double, double) 两个版本,编译器不会自动把 1 升为 double 再选后者——它认为两个候选都需要一次转换,产生歧义,报错 call to 'add' is ambiguous。
- 解决办法:显式加
static_cast,或补一个add(int, double) - 避免过度依赖用户自定义转换构造函数(比如
String(int)),它会让重载解析更难预测 - 用
clang++ -Xclang -ast-dump或 IDE 的“Go to definition”可观察实际匹配到了哪个重载
模板函数和普通重载混用要小心优先级
模板实例化出来的函数和非模板函数在重载集中地位不等:非模板函数优先于模板特化。但如果你写了 template<typename t> void foo(T)</typename> 和 void foo(std::string),传入 "hello" 字符串字面量时,foo(std::string) 会被选中;而传入 std::string{"hi"} 也一样——但若你删掉那个非模板版本,模板就会接手。
- 陷阱:加了
const引用参数后,foo(const std::string&)和template<t> foo(T&&)</t>可能因完美转发机制抢走本该给非模板的调用 - 调试建议:对关键重载点加
static_assert(!std::is_same_v<t std::string>, "don't use template for string")</t>快速暴露误匹配 - 真需要泛化 + 特化共存,考虑用
std::enable_if约束模板,而不是依赖重载顺序
类内重载 operator 常见漏掉 const 和 & 限定
重载 operator[] 或 operator* 时,如果不提供 const 版本,你就不能对 const 对象调用它;如果不区分 & 和 const& 返回,就可能让临时对象绑定到非常量引用上,引发未定义行为。
- 正确姿势:
T& operator[](size_t i);和const T& operator[](size_t i) const; - 错误例子:
auto x = container[0];在container是const时会编译不过,除非你写了const版本 - 移动语义场景下,还应考虑右值重载:
T&& operator[](size_t i) &&;,否则std::move(container)[0]仍走左值版本
-Wall -Wextra,并习惯在不确定时加 static_assert 锁定类型。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











