## [$]一.模板實參推斷
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 * 其他設施