通用引用太 “贪婪”,会抢走所有重载匹配

  • 通用引用是模板 T&&,对任何类型实参都能精确匹配;
  • 普通重载(如 int、const std::string&)需要类型转换 / 提升时,永远赢不过通用引用。

例子:

1
2
void logAndAdd(int idx);                // 普通重载
template<typename T> void logAndAdd(T&& name); // 通用引用

传 short 时:

  • 本想走 int 重载(需要类型提升)
  • 结果通用引用精确匹配,直接抢走调用,导致后续无法转成 string,编译报错。

完美转发构造函数会 “劫持” 拷贝构造

写一个带通用引用构造函数的类:

1
2
3
4
5
6
class Person {
public:
template<typename T> Person(T&& n); // 通用引用构造
Person(int idx);
Person(const Person& rhs); // 编译器生成的拷贝构造
};
  • 传 非 const 左值 Person(如 Person p; auto clone = p;)
  • 通用引用构造能生成 Person(Person&),比拷贝构造 Person(const Person&) 匹配更好
  • 结果:本该拷贝,却调用了转发构造,试图用 Person 初始化 string,直接编译崩溃。

继承体系下直接炸锅

派生类的拷贝 / 移动构造,本该调用基类的拷贝 / 移动构造:

1
2
3
class SpecialPerson : public Person {
SpecialPerson(const SpecialPerson& rhs) : Person(rhs) {}
};

结果:Person(rhs) 被解析为通用引用构造,而不是基类拷贝构造,代码直接废掉。

总结

  1. 对通用引用函数做重载,通用引用几乎会抢走所有你不想让它接的调用;

  2. 完美转发构造函数是极差的设计:

    • 劫持非 const 左值的拷贝构造;
    • 破坏派生类的拷贝 / 移动逻辑;
  3. 解决方案:永远不要在通用引用上做重载,特殊处理逻辑用别的方式实现(下一条款讲)。