網站首頁 編程語言 正文
參考《C++ Primer Plus》(第6版)中文版,Stephen Prata 著,張海龍 袁國忠譯,人民郵電出版社。C++ 使用重載解析策略來決定為函數調用使用哪一個函數定義。重載解析過程大致分為如下三步:
第 1 步:創建候選函數列表,只要求函數名一樣即可,對函數特征標以及是否為模板函數無要求;
第 2 步:在上一步的基礎上創建可行函數列表,包含特征標完全匹配的常規函數或模板函數、以及實參隱式轉換后完全匹配的常規函數或模板函數,這些都是參數數目正確的函數;
第 3 步:在上一步的基礎上確定最佳匹配函數,若有則使用它,若沒有則該函數調用失敗。
下面以一個例子來說明這個重載過程:
//全部函數原型
void may(int); //原型#1
float may(float, float = 3); //原型#2
void may(char); //原型#3
char * may(const char *); //原型#4
char may(const char &); //原型#5
template<class T> void may(const T &);//原型#6
template<class T> void may(T *); //原型#7
void may(char, double); //原型#8
void mbk(float); //原型#9
char mkk(int, char); //原型#10
int mck(char); //原型#11
double myk(float); //原型#12
void mpk(char); //原型#13
//函數調用
may('B');
//函數定義
...
重載第 1 步:創建候選函數列表。即函數名稱為 may 的常規函數和模板函數,候選函數列表如下:
//重載第1步:創建候選函數列表
void may(int); //原型#1
float may(float, float = 3); //原型#2
void may(char); //原型#3
char * may(const char *); //原型#4
char may(const char &); //原型#5
template<class T> void may(const T &);//原型#6
template<class T> void may(T *); //原型#7
void may(char, double); //原型#8
重載第 2 步:創建可行函數列表。由于整數類型 char 不能被隱式地轉換為指針類型 char *,因此函數 #4 和函數 #7 都被排除,而函數 #8 因為參數數目不匹配也會被排除。進行完全匹配時,C++ 允許下表這些無關緊要的轉換,表中 Type 表示任意類型,例如 char & 到 const char & 的轉換也包含在內,表中 Type (argument-list) 意味著用作實參的函數名和用作形參的函數指針只要返回類型和參數列表相同,就是匹配的。
實參類型 | 形參類型 |
---|---|
Type | Type & |
Type & | Type |
Type [] | Type * |
Type (argument-list) | Type (*) (argument-list) |
Type | const Type |
Type | volatile Type |
Type * | const Type * |
Type * | volatile Type * |
根據此表可知,剩下的函數中包含特征標完全匹配的常規函數 #3 和 #5、特征標完全匹配的模板函數 #6(此時 T 可以被實例化為 char)、實參隱式轉換后完全匹配的常規函數 #1 和 #2。可行函數列表如下:
//重載第2步:創建可行函數列表
void may(int); //原型#1
float may(float, float = 3); //原型#2
void may(char); //原型#3
char may(const char &); //原型#5
template<class T> void may(const T &);//原型#6
重載第 3 步:確定最佳匹配函數。通常,從最佳到最差的順序如下所述:
- 特征標完全匹配;
- 類型需經隱式提升轉換,例如 char 和 short 自動轉換為 int,float 自動轉換為 double;
- 類型需經隱式標準轉換,例如 int 轉換為 char,long 轉換為 double;
- 類型需經隱式自定義轉換,例如類中用戶定義的類型轉換。
依此規則,函數 #3 和函數 #5、函數 #6 都是特征標完全匹配的最佳匹配函數,函數 #1 需經隱式提升轉換,函數 #2 需經隱式標準轉換,由此各函數最佳匹配程度為:(#3, #5, #6) > #1 > #2。當特征標完全匹配時,又有如下規則:
- 指向非 const 數據的指針和引用優先與形參為非 const 指針和引用的函數匹配;
- 優先與非模板函數匹配;
- 同為模板函數時,優先與較具體的模板函數匹配。
依此規則,非模板函數 #3 和 #5 最佳匹配程度要高于模板函數 #6 ,即各函數最佳匹配程度為:(#3, #5) > #6 > #1 > #2。最終出現了兩個最佳匹配函數 #3 和 #5 ,因此該函數調用失敗,編譯器將報錯。
//重載第 3 步:確定最佳匹配函數
void may(char); //原型#3
char may(const char &); //原型#5
下面展開來說上述幾條完全匹配時的規則。
第 1 條:指向非 const 數據的指針和引用優先與形參為非 const 指針和引用的函數匹配,這一點需明確,const 和非 const 之間的區別只適用于指針和引用。下面 4 個函數都與函數調用是完全匹配的:
//函數原型
void recycle(int); //原型#1
void recycle(const int); //原型#2
void recycle(int &); //原型#3
void recycle(const int &);//原型#4
//函數調用
int x = 5;
recycle(x);
//函數定義
...
- 如果這 4 個函數同時存在,則無法完成重載,編譯器會報多義性匹配的錯誤;
- 如果只存在函數 #1 與 #2,則無法完成重載,編譯器會報重復定義的錯誤;
- 如果只存在函數 #1 與 #3,則無法完成重載,編譯器會報多義性匹配的錯誤;
- 如果只存在函數 #1 與 #4,則無法完成重載,編譯器會報多義性匹配的錯誤;
- 如果只存在函數 #2 與 #3,則無法完成重載,編譯器會報多義性匹配的錯誤;
- 如果只存在函數 #2 與 #4,則無法完成重載,編譯器會報多義性匹配的錯誤;
- 如果只存在函數 #3 與 #4,則函數調用時編譯器將會選擇 #3。
第 2 條:優先與非模板函數匹配,這一點比較簡單,當完全匹配的函數中,一個是非模板函數,另一個是模板函數時,非模板函數將優于模板函數,顯式具體化、顯式實例化、隱式實例化都屬于模板函數。
第 3 條:同為模板函數時,優先與較具體的模板函數匹配,找出最具體的模板的規則被稱為函數模板的部分排序規則(partial ordering rules)。這意味著顯式具體化優先于常規模板函數,都為常規模板函數時,編譯器優先選擇實例化時類型轉換更少的那一個。以下面的程序為例,調用方式 recycle(&ink) 既與模板 #1 匹配,此時 Type 將被解釋為 blot *,也與模板 #2 匹配,此時 Type 將被解釋為 blot,因此將這兩個隱式實例 recycle<blot *>(blot *) 和 recycle<blot>(blot *) 發送到可行函數池中。在選擇最佳匹配函數時,#2 被認為是更具體的,因為它已經顯式地指出,函數參數是指向 Type 的指針,相比于 #1,它對類型的要求更加地具體,在生成過程中所需要的轉換更少,因此調用方式 recycle(&ink) 實際會匹配版本 #2。
//兩個常規模板函數
template <class Type> void recycle(Type t); //原型#1
template <class Type> void recycle(Type * t); //原型#2
//調用程序包含如下代碼
struct blot {int a; char b[10];};
blot ink = {25, "spots"};
...
recycle(&ink); //使用版本#2
//函數定義
...
部分排序規則的另一個示例程序如下,它與上一個例子有異曲同工之妙。由于模板 #2 做了特定的假設:數組內容是指針,對類型的要求更加地具體,因此在調用時第一個參數若傳入指針數組 pt,則將實際匹配函數 #2。
//兩個常規模板函數
template <typename T>
void ShowArray(T arr[], int n); //原型#1
template <typename T>
void ShowArray(T * arr[], int n); //原型#2
//調用程序包含如下代碼
int things[6] = {13, 31, 103, 301, 310, 130};
int * pt[3] = {&things[0], &things[2], &things[4]};
ShowArray(things, 6); //使用版本#1
ShowArray(pt, 3); //使用版本#2
//函數定義
...
將有多個參數的函數調用與有多個參數的原型進行匹配時,編譯器必須考慮所有參數的匹配情況。如果找到比其他可行函數都合適的函數,則選擇該函數。一個函數要比其他函數都合適,其所有參數的匹配程度都必須不比其他函數差,同時至少有一個參數的匹配程度比其他函數都高。
在有些情況下,可通過編寫合適的函數調用,來引導編譯器做出程序員期望的選擇。如下所示,其中模板函數返回兩個值中較小的一個,非模板函數返回兩個值中絕對值較小的那個。第一次調用時根據重載解析策略選擇了非模板函數 #2;第二次調用時根據重載解析策略選擇了模板函數 #1 的 double 版本,屬于模板函數的隱式實例化;第三次調用的 <> 指出,編譯器應該選擇模板函數,此時編譯器會查看調用函數時的實參類型來進行實例化,也屬于模板函數的隱式實例化;第四次調用的 <int> 顯式指出,編譯器應該使用模板函數的 int 實例化版本,此時屬于模板函數的顯式實例化。
#include <iostream>
//函數#1
template<class T>
T lesser(T a, T b)
{
return a < b ? a : b;
}
//函數#2
int lesser(int a, int b)
{
a = a < 0 ? -a : a;
b = b < 0 ? -b : b;
return a < b ? a : b;
}
//函數調用
int main()
{
using namespace std;
int m = 20;
int n = -30;
double x = 15.5;
double y = 25.9;
//使用#2,結果為20
cout << lesser(m, n) << endl;
//使用#1,double隱式實例化,結果為15.5
cout << lesser(x, y) << endl;
//使用#1,int隱式實例化,結果為-30
cout << lesser<>(m, n) << endl;
//使用#1,int顯式實例化,結果為15
cout << lesser<int>(x, y) << endl;
return 0;
}
原文鏈接:https://www.cnblogs.com/young520/p/16808445.html
相關推薦
- 2022-04-10 微服務架構之服務注冊與發現實踐示例詳解_服務器其它
- 2022-08-06 python實現去除空格及tab換行符的方法_python
- 2022-01-28 Hyper集成laravel中使用的blade模板
- 2022-03-14 windows環境搭建golang的gin框架簡易教程
- 2022-05-23 Android實現手機聯系人分欄效果_Android
- 2022-05-29 python3中的類繼承你真的了解嗎_python
- 2022-09-16 C#數據庫操作的示例詳解_C#教程
- 2022-08-26 一文搞懂Go語言中文件的讀寫與創建_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支