Item27:熟悉通用引用重载的替代方法
前提
Item26 已证明:通用引用 + 普通重载 = 必出问题(劫持调用、编译报错、破坏拷贝 / 移动构造、继承失效)。
本条款给出5 种合法替代方案,从简单到高级,覆盖所有场景。
5 种通用引用重载的替代方案
1. 放弃重载,直接改函数名
- 做法:把重载函数改成不同名字(如
logAndAddName/logAndAddNameIdx) - 优点:最简单,无任何匹配风险
- 缺点:构造函数不能用(名字固定为类名),失去重载的语法便利
2. 退而求其次:传 const T&
- 做法:放弃通用引用,用 C++98 的常量左值引用
- 优点:代码简单、匹配稳定、无坑
- 缺点:效率低,无法利用移动语义、做不到完美转发
3. 按值传递(pass by value)
- 做法:形参直接传值,配合
std::move初始化成员
1 | explicit Person(std::string n) : name(std::move(n)) {} |
- 优点:无匹配冲突,代码简洁,兼顾拷贝 / 移动
- 缺点:比完美转发略低效(多一次移动 / 拷贝)
- 适用:构造函数等对效率要求没极致的场景
4. 标签分派(tag dispatch)
对外只留一个通用引用函数,内部靠编译期标签分发给重载实现
做法:
- 外层:通用引用函数,用
is_integral/remove_reference生成true_type/false_type标签 - 内层:重载
impl函数,靠标签类型决定走哪个逻辑
- 外层:通用引用函数,用
优点:保留完美转发 + 重载,完全避免匹配冲突
缺点:代码分层,稍复杂
5. std::enable_if 约束模板(最强大、最通用)
用编译期条件限制通用引用模板的启用范围,让它只匹配你想让它匹配的类型
搭配:
std::decay_t<T>:去掉引用、const/volatilestd::is_base_of:排除自身 + 派生类(解决拷贝 / 移动构造被劫持)std::is_integral:排除整型(走普通 int 重载)
优点:完美转发 + 重载共存,效率最高、最灵活
缺点:语法复杂(C++14 别名模板简化写法)
最终折中选择
追求极致效率 + 完美转发:用 tag dispatch / std::enable_if
优点:无临时对象、性能最优
缺点:编译报错晦涩(可加
static_assert优化提示)追求简单稳定 + 易读:用 const T& / 按值传递
优点:报错清晰、代码好写
缺点:效率略低
总结
- 通用引用不能直接重载,替代方案:改名、const T&、传值、标签分派、enable_if 约束
std::enable_if是唯一能让通用引用 + 重载合法共存的方法,靠条件限制匹配范围- 高效(完美转发)和易用(简单方案)需要根据场景折中选择
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 肖恩的博客!
评论
