C++ 函式匹配 過載函式的呼叫規則
學習《C++ Primer》一書中,函式匹配這一節內容資訊較多,現擷取重點內容記錄於此。便於你對本文內容更好的理解,你需對型別提升、算術型別轉換以及頂層cosnt,底層const有一定的瞭解。
多數情況下,我們可以很容易的判斷出該會呼叫哪一個過載函式,例如,呼叫的過載函式之間形引數量不同,形參的型別有明顯的區別等。但是,當幾個過載函式形引數量相等、具有預設形參以及形參又可以發生型別轉換時,判斷會呼叫哪個過載函式就顯得不那麼明瞭。瞭解過載函式的呼叫規則有助於我們的判斷。
函式匹配可以劃分為三個步驟,分別為:選定候選函式,選定可行函式,尋找最佳匹配。以下面的一組過載函式為例:
voidf(); void f(int); void f(int,int); void f(double,double = 3,14); f(3.14); //呼叫 void f(double,double = 3.14) f(10,20); //呼叫 void f(int,int) f(10,3.14); //錯誤,二義性呼叫
一、候選函式
函式匹配的第一步是選定本次呼叫對應的過載函式集,集合中的函式稱為候選函式。候選函式具備兩個特徵:一是與被呼叫的函式同名,二是其宣告在呼叫點可見。
f(3.14); //具有4個候選函式 f(10 , 20); //具有4個候選函式 f(10 , 3.14); //具有4個候選函式
二、可行函式
函式匹配第二步是考察本次呼叫提供的實參,然後從候選函式中選出能被這組實參呼叫的函式,這些新選出來的函式稱為可行函式。可行函式也具備兩個特徵:一是形參引數與本次呼叫提供的實引數量相等,二是每個實參型別與對應的形參型別相同,或者能轉換成形參的型別。
f(3.14); //具有2個可行函式,分別為 void f(int) 和 void f(double,double = 3.14) f(10 , 20); //具有2個可行函式,分別為 void f(int,int) 和 void f(double,double = 3.14) f(10 , 3.14); //具有2個可行函式,分別為 void f(int,int) 和 void f(double,double = 3.14)
函式匹配要想成功,必須具有可行函式,但具有可行函式,函式匹配卻不一定能成功,產生該問題的原因就是下面將要說明的最佳匹配中的二義性呼叫問題。
三、最佳匹配
函式匹配的第三步是從可行函式中選擇與本次呼叫最匹配的函式。在這一過程,逐一檢查函式呼叫提供的實參,尋找形參型別與實參型別最匹配的那個可行函式。為了確定最佳匹配,編譯器將實參型別到形參型別的轉化劃分成幾個等級(從 1 到 5 匹配度逐步降低),具體排序如下所示:
1.精確匹配,包括以下情況(它們具有相同且最高的匹配度):
- 實參型別和形參型別相同。
- 實參從陣列型別或函式型別轉換成對應的指標型別
- 向實參新增頂層const或者從實參中刪除頂層const
2.通過const轉化實現匹配(底層cosnt的轉換)
3.通過型別提升實現的匹配
4.通過算術型別轉換實現的匹配
5.通過類型別轉換實現的匹配
f(3.14); //可以通過算術型別轉化實現匹配,匹配的重在函式有且只有一個,所以最佳匹配為f(int) f(10,20); //有且只有一個精確匹配的過載函式,所以最佳匹配為 f(int,int) f(10,3.14); //可以通過算術型別轉化實現匹配,但匹配度相同的過載函式有兩個,分別為 void f(int,int) 和 void f(double,double = 3.14),二義性呼叫,沒有最佳匹配
只要不存在二義性問題,且具有可行函式,那麼一定可以尋找到最佳匹配,函式匹配就能成功,反之,函式匹配成功也一定具有最佳匹配。
讓我們來看些更細節的例子,來進一步理解最佳匹配
1.要進行轉換的實引數量,會影響匹配度
#include<iostream> using namespace std; void f2(int, int) { cout << "1" << endl; } void f2(int, double) { cout << "2" << endl; } int main() { f2(3.14, 3.14); //具有最佳匹配,呼叫第一個過載函式的話,兩個實參都需要進行算術型別轉換,而掉用第二個過載函式的話只需要對一個實參進行算術型別轉換。
//第二個的匹配度更高,故呼叫第二個過載函式。 return 0; }
2.需要型別提升和算術型別轉換的匹配
#include<iostream> using namespace std; void f3(int) { cout << "1" << endl; } void f3(double) { cout << "2" << endl; } int main() { short val = 10; f3(val); //具有最佳匹配,呼叫第一個過載函式需要進行型別提升,呼叫第二個過載函式需要進行算術型別轉換(實際上是先型別提升,再算術型別轉換) //第一個過載函式有更高的匹配度,故呼叫第一個過載函式。 return 0; }
3.底層cosnt對最佳匹配的影響
#include<iostream> using namespace std; void f4(int*) { cout << "1" << endl; } void f4(const int*) { cout << "2" << endl; } int main() { int val2 = 10; const int val3 = 10; f4(&val2); //可以和 void f4(int*) 精確匹配,也可以通過cosnt轉換和 void f4(const int*)實現匹配,精確匹配更優,故呼叫第一個過載函式 f4(&val3); //可以和 void f4(const int*) 精確匹配,有且只有這一個過載函式可匹配,故呼叫第二個過載函式 return 0; }