說起函數重載,我不由得想起了C++的“多態”特性。多態又分為靜態(編譯時)多態和動態(運行時)多態,靜態多態即為函數重載,動態多態則是虛函數機制。虛函數水較深,先不討論,今天我們來看一下函數重載、作用以及要避免的一些坑(尤其是二義性錯誤)。
一、先來review一下 函數重載的概念
1. 函數名相同;
2. 形參類型和數量不同;
3. 不關返回值的事。
二、如何匹配要使用那個函數呢?
在C++ primary 中文5版 P217我們可以發現端倪。
1. 選定候選函數集 : ① 與被調用函數同名; ② 聲明在調用點可見。=》沒有,返回無匹配錯誤
2. 選定可行函數集:考察候選函數的參數列表,找出參數數量相同、且各個參數類型相同或者可以轉換為形參的函數;=》沒有,返回無匹配錯誤
3. 選出最佳匹配函數:層層選拔之后,如果幸存者函數有幾個的話(不太可能),我們就要找出最最最合適的那一個,怎么找?實參和形參類型最接近的那一個。=》沒有,返回無匹配錯誤
三、函數重載二義性
二義性是指在編譯過程中無法找出最匹配的函數,或者說編譯器在函數匹配過后還是有多個函數滿足要求,無法確定該執行那一個引發的錯誤。
具體表現:
1. 參數數目引發的歧義
1 int get(){ 2 return 5; 3 } 4 int get(int a = 5){ 5 return a; 6 } 7 //調用get()
//不給參數和有默認參數會造成歧義。
2. 參數隱式轉換引發的歧義
1 int get(int m){ 2 return m; 3 } 4 5 long get(long m){ 6 return m; 7 }
//double d = 1.234;
//調用get(d);double既可以隱式轉換未long,也可以是int,或者說一般的數值類型之間都可以進行隱式類型轉換,故無法確定那一個更加匹配。
1 int get(int a){ 2 return a; 3 } 4 int get(int &a){ 5 return a; 6 } 7 //int m = 10; 8 //調用get(m)
//傳值和傳引用都可以完成調用,只不過值傳遞有拷貝的開銷,而引用傳遞不會有。
3. 類型型相關歧義(較深內容,不容易理解)
P517-520,艱澀難懂,需要自己琢磨。
另外,我發現在P517的一個例子,是錯誤的,並未發生歧義,不會導致運行錯誤。
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 struct B; 6 struct A { 7 A() = default; 8 A(const B&){ 9 //轉換B為A 10 cout << "A內部將B轉換為A" << endl; 11 }; 12 //... 13 string name = "A"; 14 }; 15 16 struct B { 17 B() = default; 18 operator A() const{ 19 //類類型轉換,B->A 20 cout << "B 類類型轉換為 A" << endl; 21 }; 22 //... 23 string name = "B"; 24 }; 25 26 A f(const A&) { 27 //... 28 } 29 30 int main() 31 { 32 B b; 33 A a = f(b); 34 }
(由g++運行)
四、如何避免/解決
一是我們要注意默認參數,二是要考慮類型轉換,使用explicit可以阻止隱式類型轉換。