深入了解C++函數(shù)重載解析策略
參考《C++ Primer Plus》(第6版)中文版,Stephen Prata 著,張海龍 袁國忠譯,人民郵電出版社。C++ 使用重載解析策略來決定為函數(shù)調(diào)用使用哪一個(gè)函數(shù)定義。重載解析過程大致分為如下三步:
第 1 步:創(chuàng)建候選函數(shù)列表,只要求函數(shù)名一樣即可,對(duì)函數(shù)特征標(biāo)以及是否為模板函數(shù)無要求;
第 2 步:在上一步的基礎(chǔ)上創(chuàng)建可行函數(shù)列表,包含特征標(biāo)完全匹配的常規(guī)函數(shù)或模板函數(shù)、以及實(shí)參隱式轉(zhuǎn)換后完全匹配的常規(guī)函數(shù)或模板函數(shù),這些都是參數(shù)數(shù)目正確的函數(shù);
第 3 步:在上一步的基礎(chǔ)上確定最佳匹配函數(shù),若有則使用它,若沒有則該函數(shù)調(diào)用失敗。
下面以一個(gè)例子來說明這個(gè)重載過程:
//全部函數(shù)原型
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
//函數(shù)調(diào)用
may('B');
//函數(shù)定義
...
重載第 1 步:創(chuàng)建候選函數(shù)列表。即函數(shù)名稱為 may 的常規(guī)函數(shù)和模板函數(shù),候選函數(shù)列表如下:
//重載第1步:創(chuàng)建候選函數(shù)列表 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 步:創(chuàng)建可行函數(shù)列表。由于整數(shù)類型 char 不能被隱式地轉(zhuǎn)換為指針類型 char *,因此函數(shù) #4 和函數(shù) #7 都被排除,而函數(shù) #8 因?yàn)閰?shù)數(shù)目不匹配也會(huì)被排除。進(jìn)行完全匹配時(shí),C++ 允許下表這些無關(guān)緊要的轉(zhuǎn)換,表中 Type 表示任意類型,例如 char & 到 const char & 的轉(zhuǎn)換也包含在內(nèi),表中 Type (argument-list) 意味著用作實(shí)參的函數(shù)名和用作形參的函數(shù)指針只要返回類型和參數(shù)列表相同,就是匹配的。
| 實(shí)參類型 | 形參類型 |
|---|---|
| Type | Type & |
| Type & | Type |
| Type [] | Type * |
| Type (argument-list) | Type (*) (argument-list) |
| Type | const Type |
| Type | volatile Type |
| Type * | const Type * |
| Type * | volatile Type * |
根據(jù)此表可知,剩下的函數(shù)中包含特征標(biāo)完全匹配的常規(guī)函數(shù) #3 和 #5、特征標(biāo)完全匹配的模板函數(shù) #6(此時(shí) T 可以被實(shí)例化為 char)、實(shí)參隱式轉(zhuǎn)換后完全匹配的常規(guī)函數(shù) #1 和 #2??尚泻瘮?shù)列表如下:
//重載第2步:創(chuàng)建可行函數(shù)列表 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 步:確定最佳匹配函數(shù)。通常,從最佳到最差的順序如下所述:
- 特征標(biāo)完全匹配;
- 類型需經(jīng)隱式提升轉(zhuǎn)換,例如 char 和 short 自動(dòng)轉(zhuǎn)換為 int,float 自動(dòng)轉(zhuǎn)換為 double;
- 類型需經(jīng)隱式標(biāo)準(zhǔn)轉(zhuǎn)換,例如 int 轉(zhuǎn)換為 char,long 轉(zhuǎn)換為 double;
- 類型需經(jīng)隱式自定義轉(zhuǎn)換,例如類中用戶定義的類型轉(zhuǎn)換。
依此規(guī)則,函數(shù) #3 和函數(shù) #5、函數(shù) #6 都是特征標(biāo)完全匹配的最佳匹配函數(shù),函數(shù) #1 需經(jīng)隱式提升轉(zhuǎn)換,函數(shù) #2 需經(jīng)隱式標(biāo)準(zhǔn)轉(zhuǎn)換,由此各函數(shù)最佳匹配程度為:(#3, #5, #6) > #1 > #2。當(dāng)特征標(biāo)完全匹配時(shí),又有如下規(guī)則:
- 指向非 const 數(shù)據(jù)的指針和引用優(yōu)先與形參為非 const 指針和引用的函數(shù)匹配;
- 優(yōu)先與非模板函數(shù)匹配;
- 同為模板函數(shù)時(shí),優(yōu)先與較具體的模板函數(shù)匹配。
依此規(guī)則,非模板函數(shù) #3 和 #5 最佳匹配程度要高于模板函數(shù) #6 ,即各函數(shù)最佳匹配程度為:(#3, #5) > #6 > #1 > #2。最終出現(xiàn)了兩個(gè)最佳匹配函數(shù) #3 和 #5 ,因此該函數(shù)調(diào)用失敗,編譯器將報(bào)錯(cuò)。
//重載第 3 步:確定最佳匹配函數(shù) void may(char); //原型#3 char may(const char &); //原型#5
下面展開來說上述幾條完全匹配時(shí)的規(guī)則。
第 1 條:指向非 const 數(shù)據(jù)的指針和引用優(yōu)先與形參為非 const 指針和引用的函數(shù)匹配,這一點(diǎn)需明確,const 和非 const 之間的區(qū)別只適用于指針和引用。下面 4 個(gè)函數(shù)都與函數(shù)調(diào)用是完全匹配的:
//函數(shù)原型 void recycle(int); //原型#1 void recycle(const int); //原型#2 void recycle(int &); //原型#3 void recycle(const int &);//原型#4 //函數(shù)調(diào)用 int x = 5; recycle(x); //函數(shù)定義 ...
- 如果這 4 個(gè)函數(shù)同時(shí)存在,則無法完成重載,編譯器會(huì)報(bào)多義性匹配的錯(cuò)誤;
- 如果只存在函數(shù) #1 與 #2,則無法完成重載,編譯器會(huì)報(bào)重復(fù)定義的錯(cuò)誤;
- 如果只存在函數(shù) #1 與 #3,則無法完成重載,編譯器會(huì)報(bào)多義性匹配的錯(cuò)誤;
- 如果只存在函數(shù) #1 與 #4,則無法完成重載,編譯器會(huì)報(bào)多義性匹配的錯(cuò)誤;
- 如果只存在函數(shù) #2 與 #3,則無法完成重載,編譯器會(huì)報(bào)多義性匹配的錯(cuò)誤;
- 如果只存在函數(shù) #2 與 #4,則無法完成重載,編譯器會(huì)報(bào)多義性匹配的錯(cuò)誤;
- 如果只存在函數(shù) #3 與 #4,則函數(shù)調(diào)用時(shí)編譯器將會(huì)選擇 #3。
第 2 條:優(yōu)先與非模板函數(shù)匹配,這一點(diǎn)比較簡單,當(dāng)完全匹配的函數(shù)中,一個(gè)是非模板函數(shù),另一個(gè)是模板函數(shù)時(shí),非模板函數(shù)將優(yōu)于模板函數(shù),顯式具體化、顯式實(shí)例化、隱式實(shí)例化都屬于模板函數(shù)。
第 3 條:同為模板函數(shù)時(shí),優(yōu)先與較具體的模板函數(shù)匹配,找出最具體的模板的規(guī)則被稱為函數(shù)模板的部分排序規(guī)則(partial ordering rules)。這意味著顯式具體化優(yōu)先于常規(guī)模板函數(shù),都為常規(guī)模板函數(shù)時(shí),編譯器優(yōu)先選擇實(shí)例化時(shí)類型轉(zhuǎn)換更少的那一個(gè)。以下面的程序?yàn)槔{(diào)用方式 recycle(&ink) 既與模板 #1 匹配,此時(shí) Type 將被解釋為 blot *,也與模板 #2 匹配,此時(shí) Type 將被解釋為 blot,因此將這兩個(gè)隱式實(shí)例 recycle<blot *>(blot *) 和 recycle<blot>(blot *) 發(fā)送到可行函數(shù)池中。在選擇最佳匹配函數(shù)時(shí),#2 被認(rèn)為是更具體的,因?yàn)樗呀?jīng)顯式地指出,函數(shù)參數(shù)是指向 Type 的指針,相比于 #1,它對(duì)類型的要求更加地具體,在生成過程中所需要的轉(zhuǎn)換更少,因此調(diào)用方式 recycle(&ink) 實(shí)際會(huì)匹配版本 #2。
//兩個(gè)常規(guī)模板函數(shù)
template <class Type> void recycle(Type t); //原型#1
template <class Type> void recycle(Type * t); //原型#2
//調(diào)用程序包含如下代碼
struct blot {int a; char b[10];};
blot ink = {25, "spots"};
...
recycle(&ink); //使用版本#2
//函數(shù)定義
...
部分排序規(guī)則的另一個(gè)示例程序如下,它與上一個(gè)例子有異曲同工之妙。由于模板 #2 做了特定的假設(shè):數(shù)組內(nèi)容是指針,對(duì)類型的要求更加地具體,因此在調(diào)用時(shí)第一個(gè)參數(shù)若傳入指針數(shù)組 pt,則將實(shí)際匹配函數(shù) #2。
//兩個(gè)常規(guī)模板函數(shù)
template <typename T>
void ShowArray(T arr[], int n); //原型#1
template <typename T>
void ShowArray(T * arr[], int n); //原型#2
//調(diào)用程序包含如下代碼
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
//函數(shù)定義
...
將有多個(gè)參數(shù)的函數(shù)調(diào)用與有多個(gè)參數(shù)的原型進(jìn)行匹配時(shí),編譯器必須考慮所有參數(shù)的匹配情況。如果找到比其他可行函數(shù)都合適的函數(shù),則選擇該函數(shù)。一個(gè)函數(shù)要比其他函數(shù)都合適,其所有參數(shù)的匹配程度都必須不比其他函數(shù)差,同時(shí)至少有一個(gè)參數(shù)的匹配程度比其他函數(shù)都高。
在有些情況下,可通過編寫合適的函數(shù)調(diào)用,來引導(dǎo)編譯器做出程序員期望的選擇。如下所示,其中模板函數(shù)返回兩個(gè)值中較小的一個(gè),非模板函數(shù)返回兩個(gè)值中絕對(duì)值較小的那個(gè)。第一次調(diào)用時(shí)根據(jù)重載解析策略選擇了非模板函數(shù) #2;第二次調(diào)用時(shí)根據(jù)重載解析策略選擇了模板函數(shù) #1 的 double 版本,屬于模板函數(shù)的隱式實(shí)例化;第三次調(diào)用的 <> 指出,編譯器應(yīng)該選擇模板函數(shù),此時(shí)編譯器會(huì)查看調(diào)用函數(shù)時(shí)的實(shí)參類型來進(jìn)行實(shí)例化,也屬于模板函數(shù)的隱式實(shí)例化;第四次調(diào)用的 <int> 顯式指出,編譯器應(yīng)該使用模板函數(shù)的 int 實(shí)例化版本,此時(shí)屬于模板函數(shù)的顯式實(shí)例化。
#include <iostream>
//函數(shù)#1
template<class T>
T lesser(T a, T b)
{
return a < b ? a : b;
}
//函數(shù)#2
int lesser(int a, int b)
{
a = a < 0 ? -a : a;
b = b < 0 ? -b : b;
return a < b ? a : b;
}
//函數(shù)調(diào)用
int main()
{
using namespace std;
int m = 20;
int n = -30;
double x = 15.5;
double y = 25.9;
//使用#2,結(jié)果為20
cout << lesser(m, n) << endl;
//使用#1,double隱式實(shí)例化,結(jié)果為15.5
cout << lesser(x, y) << endl;
//使用#1,int隱式實(shí)例化,結(jié)果為-30
cout << lesser<>(m, n) << endl;
//使用#1,int顯式實(shí)例化,結(jié)果為15
cout << lesser<int>(x, y) << endl;
return 0;
}到此這篇關(guān)于深入了解C++函數(shù)重載解析策略的文章就介紹到這了,更多相關(guān)C++函數(shù)重載解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中的頭文件與Extern(外部函數(shù)調(diào)用)方式
這篇文章主要介紹了C++中的頭文件與Extern(外部函數(shù)調(diào)用)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Qt串口通信開發(fā)之QSerialPort模塊詳細(xì)使用方法與實(shí)例
這篇文章主要介紹了Qt串口通信開發(fā)之QSerialPort模塊詳細(xì)使用方法與實(shí)例,需要的朋友可以參考下2020-03-03
C++ 模擬實(shí)現(xiàn)list(迭代器)實(shí)現(xiàn)代碼
這篇文章主要介紹了C++ 模擬實(shí)現(xiàn)list(迭代器)實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05
關(guān)于VS2022不能使用<bits/stdc++.h>的解決方案(萬能頭文件)
#include<bits/stdc++.h>包含了目前 C++ 所包含的所有頭文件,又稱萬能頭文件,那么如何在VS2022中使用萬能頭呢?下面小編給大家代理了關(guān)于VS2022不能使用<bits/stdc++.h>的解決方案(萬能頭文件),感興趣的朋友一起看看吧2022-03-03
C語言實(shí)現(xiàn)靜態(tài)版通訊錄的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C語言實(shí)現(xiàn)一個(gè)簡單的靜態(tài)版通訊錄,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語言有一定幫助,需要的可以參考一下2022-08-08

