本文主要总结一下涉及到函数模板的函数重载,其中的一些知识点。
函数重载
什么样的 同名(signatrue) 函数构成函数重载?
- 不同参数个数
- 不同参数类型
- 不同参数顺序
- 类成员函数的
const
和non-const
- 指针和引用的
const
和non-const
- 函数模板(特化)与普通函数。
f(int)
f(const int) // error
f(int &)
f(int const&)
template<typename T> void f(T a)
template<> void f<int>(int a)
重载解析
对于一个命名函数的调用,通常会使用下列的处理方法:
- 确定同名的函数,组成重载(候选)集;
- 模板实参演绎;
- 函数匹配,选择最优函数。
- 检查匹配结果。如果最优函数有多个,二义性; 最优函数访问私有成员等等。
实参演绎
在实参演绎过程中,每个实参-参数对的分析都是独立的。
template<typename T> void f(T);
template<typename T> void g(T&);
template<typename T> void r(T&& param);
double x[20];
int const seven = 7;
int y = 4;
int& ry = y;
f(x); // T 被演绎成 double* , double [] decay 成 double*
f(seven); // T 被演绎成 int,const丢失
f(7); // T 被演绎成 int.
f(ry) // T 被演绎成 int.
g(x); // T 被演绎成 double[20]
g(seven); // T 被演绎成 int const
g(7); // ERROR!!! 不能把右值 7 传给 int&
g(ry); // T 被演绎成 int
r(seven); // seven 是左值,T和 param 都被演绎成 int const&
r(y); // y是左值, T 和 param 都被演绎成 int&
r(ry); // ry 是左值, T 和 param 都被演绎成 int&
r(7); // 7 是右值, 所以 T 是 int, param 是 int&&
具体的实参演绎(模板类型推导)规则,可以参照 模板类型推导。
SFINAE原则: 实参演绎的过程中如果发生不匹配的,或者匹配后使用错误的类型操作的,并不代表发生了ERROR,只需跳过这个模板,寻找下一个模板进行匹配即可。
函数匹配
匹配优先级(高到低) | 说明 |
---|---|
完美匹配 | 参数类型 和 实参类型完全相同,或者参数的类型是引用(const 引用)。 const 和non-const 属性也会存在完全匹配的差异。 |
decay | 如 数组类型 转化(decay)为指针类型; 或者 int 转化为 const int |
提升匹配 | 占位少的整数类型转换为占位多的类型。如bool -- int 或者 float -- double |
类型转换的匹配 | 包含任何种类的标准类型转换。如int -- float ,但不包含隐式调用的类型转换运算符和单参数构造函数(所以要加explicit禁用) |
用户自定义类型转换匹配 | 允许任何可行的隐式类型转换 |
函数模板特化的标准匹配 | |
函数模板的匹配 | |
省略号的匹配 |