## [$]一.模板实参推断
1. 函数实参的顶层 `const` 会被忽略,而底层 `const` 会保留。
#### 例1
函数实参为 `T` 时,传入 `const int` 的数据,则 `T=int` 。
函数实参为 `T &` 时,传入 `const int` 的左值,则 `T=const int` 。
函数实参为 `T &&` 时,传入 `const int` 的右值,则 `T=const int` 。
2. 模板推断中,类型转换是有限的:
+ 左值到右值的转换。例如,传入整型变量,编译器会推断为 `int` 而不是 `int &`,除非函数实参加了引用 `T &`。
+ `const` 转换:非底层 `const` 转换成底层 `const` 。
+ 数组转换成指向数组第一个元素的指针。
+ 函数转换成函数指针。
其他的转换不会用于模板类型的函数参数,例如算术转换、派生类到基类的转换、用户定义的转换。
3. 相同的模板实参用于多个函数参数时, **实参必须严格一致** 。正常的函数参数不受影响。
4. 当用户指定显式的实参时,正常的类型转换便会应用于显式实参。
#### 例2
```c++
template <typename T>
T add(const T &t1,const T &t2)
{
return t1+t2;
}
```
函数调用:
```c++
add(298,700);//ok
long l = 298;
add(l,700);//wrong:编译器从第一个参数中推断出T=long,从第二个参数中推断出T=int。
add<int>(l,700);//ok:正常的类型转换会应用于显式实参
```
5. [11+]当参数被指定为右值引用时,可能会发生引用折叠,导致该参数既可以接受左值,也可以接受右值。
#### [11+]例3
函数实参为 `T &&` 时,传入 `int` 的纯右值,则 `T=int` 。
函数实参为 `T &&` 时,传入 `int` 的左值,则 `T=int &` ,但此时发生引用折叠, `&&&` 被折叠为 `&`,最终 `T &&` 是一个左值引用 `int &`。
函数实参为 `T &&` 时,传入将亡值 `int &&` ,则 `T=int &&` ,但此时发生引用折叠, `&&&&` 被折叠为 `&&`,最终 `T &&` 是一个右值引用 `int &&`。
## [$]二.函数指针与实参推断
编译器会通过指针的类型来进行参数的推断,如果参数无法匹配或出现二义性,则编译器报错。
#### 例4
```c++
//add的定义参见例2
typedef long (*Fp)(const long &,const long &);
Fp f= add;
long l = 298;
f(l,700);//ok
```
## [$]三.重载与模板
1. 候选的函数模板总是可行的,因为模板实参推断会排除任何不可行的模板。
2. 当多个函数提供了同样好的匹配:(越特例化越优先)
+ 如果同样好的函数中只有一个是非模板函数,则选择此函数。
+ 如果同样好的函数中没有非模板函数,而有多个函数模板,且其中一个模板比其他模板更特例化,则选择此模板。
+ 否则,此调用是二义性的。
#### 例5
假设有下面一组函数:
```c++
template <typename T> void print(const T &t);
template <typename T> void print(T *p);
void print(const string &s);
```
1. 当通过以下方式调用时,匹配第一个版本。
```c++
print(2333);
```
2. 当通过以下方式调用时,生成两个可行的匹配:
+ `void print(const string &t);`
+ `void print(const string &s);`
正常的模板解析过程无法区分这两个函数。但是,根据重载函数模板的特殊规则,`const string &s` 不是模板类型,因此这里匹配第二个版本。
```c++
string s = "2020";
print(s);
```
3. 当通过以下方式调用时,生成两个可行的匹配:
+ `void print(const string *&t);`
+ `void print(string *p);`
而第一个版本需要非 `const` 到 `const` 的转换,因此这里匹配第二个版本。
```c++
string s = "2020";
print(&s);
```
4. 当通过以下方式调用时,生成了两个可行的匹配:
+ `void print(const string *&t);`
+ `void print(const string *p);`
正常的模板解析过程无法区分这两个函数。但是,根据重载函数模板的特殊规则,`const T &t` 适用于任意类型,而 `T *p` 适用于指针。也就是说,第二个版本更特例化,所以匹配到第二个版本。
```c++
string s = "2020";
const string *ps = &s;
print(ps);
```
5. 当通过以下方式调用时,生成了三个可行的匹配:
+ `void print(const char (&t)[10]);` 这里参数是一个数组的引用。
+ `void print(const char *p);`
+ `void print(const string *&s);`
第三个版本需要进行一次用户定义的类型转换,第二个版本需要进行数组到指针的转换,第一个版本不需要转换。但是数组到指针的转换被认为是精确匹配,所以只排除第三个版本。
与上一个例子一样,`const T &t` 适用于任意类型,而 `T *p` 适用于指针。也就是说,第二个版本更特例化,所以匹配到第二个版本。
```c++
print("Hello");
```
- 阅读说明
- 1.1 概述
- C++基础
- 1.2 变量与常量
- 1.2.1 变量
- 1.2.2 字面值常量
- 字符型常量
- 数值型常量
- 1.2.3 cv限定符
- 1.3 作用域
- 1.3.1 标识符
- 1.3.2 *命名空间
- 1.3.3 作用域
- 1.3.4 可见性
- 1.4 数据类型
- 1.4.1 概述
- 1.4.2 处理类型
- 类型别名
- * auto说明符
- * decltype说明符
- 1.4.3 数组
- 1.4.4 指针
- 1.4.5 引用
- 1.5 表达式
- 1.5.1 概述
- 1.5.2 值的类别
- 1.5.3 *初始化
- 1.5.4 运算符
- 算术运算符
- 逻辑和关系运算符
- 赋值运算符
- 递增递减运算符
- 成员访问运算符
- 位运算符
- 其他运算符
- 1.5.5 *常量表达式
- 1.5.6 类型转换
- 第2章 面向过程编程
- 2.1 流程语句
- 2.1.1 条件语句
- 2.1.2 循环语句
- 2.1.3 跳转语句
- 2.1.4 *异常处理
- 2.2 函数
- 2.2.1 概述
- 2.2.2 函数参数
- 2.2.3 内置函数
- 2.2.4 函数重载
- 2.2.5 * 匿名函数
- 2.3 存储和生命期
- 2.3.1 生命周期与存储区域
- 2.3.2 动态内存
- 2.4 *预处理命令
- 第3章 面向对象编程
- 3.1 概述
- 3.2 类和对象
- 3.3 成员
- 3.3.1 访问限制
- 3.3.2 常成员
- 3.3.3 静态成员
- 3.3.4 成员指针
- 3.3.5 this指针
- 3.4 特殊的成员函数
- 3.4.1 概述
- 3.4.2 构造函数
- 3.4.3 析构函数
- 3.4.4 拷贝语义
- 3.4.5 * 移动语义
- 3.5 友元
- 3.6 运算符重载与类型转换
- 3.6.1 概述
- 3.6.2 重载方法
- 3.6.3 类型转换
- 3.7 继承与多态性
- 3.7.1 概述
- 3.7.2 派生类
- 3.7.3 子类型
- 3.7.4 虚基类
- 3.7.5 虚函数
- 3.7.6 抽象类
- 3.8 模板与泛型
- 3.8.1 概述
- 3.8.2 模板类型
- 3.8.3 *模板参数
- 3.8.4 *模板编译
- 3.8.5 *模板推断
- 3.8.6 *实例化与特例化
- 第4章 C++标准库
- 4.1 概述
- 4.2 输入输出流
- 4.2.1 概述
- 4.2.2 *流的状态
- 4.2.3 *常用流
- 4.2.4 *格式化I/O
- 4.2.5 *低级I/O
- 4.2.6 *随机访问
- 4.3 *C输入输出
- 4.3.1 *字符输入输出
- 4.3.2 *格式化输入输出
- 4.4 * 容器
- 4.4.1 * 概述
- 4.4.2 * 基本操作
- 4.4.3 * 顺序容器
- 4.4.4 * 迭代器
- 4.4.5 * 容器适配器
- 4.5 * 泛型算法
- 4.6 * 内存管理
- 4.6.1 * 自动指针
- 4.7 * 其他设施