C 函式過載與函式匹配

2022-08-26 06:48:10 字數 3305 閱讀 6442

《c++ primer》筆記,整理關於函式過載與函式匹配的筆記。

void func(int a); //原函式

void func(double a); //正確:形參型別不同

void func(int a, int b); // 正確:形參個數不同

int func(int a); //錯誤:只有返回型別不同

typedef int int32;

void func(int32 a); //與原函式等價:形參型別相同

void func(const int a); //與原函式等價:頂層 const 將被忽略

void func(int); //與原函式等價:只是省略了形參名字

函式過載有如下的規則:

其中返回型別不同時編譯時會出錯,而型別別名、項層const、省略形參名字只是重複宣告而已,只要不定義,編譯就不會出錯,比如:

//只定義了其中乙個

void func(int a);

void func(const int a) {}

函式匹配的第一步便是名字查詢(name lookup),確定候選函式

名字查詢有兩方面:

所有函式呼叫都會進行常規查詢,只有函式的實參包括類型別物件或指向類型別物件的指標/引用的時候,才會進行實參決定的查詢。

常規查詢

void func(int a);   //1

namespace n

//2void func(double a) {} //3

...void test1()

void test2()

...}

從函式被呼叫的區域性作用域開始,逐漸向上層尋找被呼叫的名字,一旦找到就停止向上尋找,將找到的所有名字加入候選函式。

此外,using語句可以將其他作用域的名字引用到當前作用域。

adl查詢

void func() {} //1

//第乙個實參所在命名空間

namespace name1 //2

};void func(t) {} //3

}//第二個實參的間接父類所在命名空間

namespace name00 //4

};void func() {} //5

}//第二個實參父類所在命名空間

namespace name0 //6

};void func() {} //7

}//第二個實參所在命名空間

namespace name2 //8

};void func(t) {} //9

}void test()

從第乙個類型別引數開始,依次遍歷所有類型別引數。對於每乙個引數,進入其型別定義所在的作用域(類內友元函式也包括在內),並依次進入其基類、間接基類……定義所在的作用域,查詢同名函式,並加入候選函式。

注意:在繼承體系中上公升的過程中,不會因為找到同名函式就停止上公升,這不同於常規查詢。

類中的運算子過載也遵循 adl 查詢,其候選函式集既包括成員函式,也應該包括非成員函式。

namespace n

//1};

void operator+(a &a, int a) {} //2

};void operator+(a &a, int a) {} //3

void test()

第二步便是從候選函式中選出可行函式,選擇的標準如下:

//以下為候選函式

void func(int a, double b) {} //可行函式

void func(int a, int b) {} //可行函式:實參可轉化成形參型別

int func(int a, double b) {} //可行函式

void func(int a) {} //非可行函式:形參數量不匹配

void func(int a, int b) {} //非可行函式:實參不能轉換成形參

void test()

從可行函式中選擇最匹配的函式,如果有多個形參,則最佳匹配條件為:

否則,發生二義性呼叫錯誤。

//可行函式

void func(int a, float b) {}

void func(int a, int b) {}

void test()

為了確定最佳匹配,實參型別到形參型別的轉換等級如下:

精確匹配:

通過const轉換實現的匹配。

通過型別提公升實現的匹配。

通過算術型別轉換或指標轉換實現的匹配。

通過類型別轉換實現的匹配。

一般不會存在這個階段不會同時存在兩個以上的精確匹配,因為兩個精確的匹配在本質上是等價的,在定義過載函式時,編譯器可能就報出重定義的錯誤了。

挑幾個重點的來詳細說一下。

指標轉換實現的匹配

類型別轉換實現的匹配

兩個型別提供相同的型別轉換將產生二義性問題。

struct b;

struct a

;struct b

;a f(const a&);

b b;

a a = f(b); //二義性錯誤:f(b::operator a()) 還是 f(a::a(const b&))

a a1 = f(b.operator a()); //正確:使用 b 的型別轉換運算

a a2 = f(a(b)); //正確:使用 a 的建構函式

類當中定義了多個引數都是算術型別的建構函式或型別轉換運算子,也會產生二義性問題。

struct a

;void f(long double);

a a;

f(a); //二義性錯誤:f(a::operator int()) 還是 f(a::operator double())?

long l;

a a2(l); //二義性錯誤:a::a(int) 還是 a::a(double)?

short s;

a a3(s); //正確:使用 a::a(int)

當我們使用兩個使用者定義的型別轉換時,如果轉換函式之前或之後存在標準型別轉換,則標準型別轉換將決定最佳匹配到底是哪個。

部分參考:

c 函式過載與匹配

匹配規則 精確匹配 常量版本匹配 變數提公升 算數 指標轉換 類型別轉換 當我們為函式過載,並呼叫過載後的函式時,編譯器會自動根據引數型別進行匹配與轉換但是此時 容易出現二義性錯誤,函式引數轉換與型別提公升容易混淆,例如當我們同時過載了float double 版本的函式時,傳入乙個int型實參,就...

C 學習 函式過載與匹配

在c 中,函式過載一直是乙個常用且重要的東西,但其中也是有很多坑。實際上,在g 處理函式過載的時候,假設有下面兩個函式 void fun int a,int b void fun int a,char b 很明顯這是兩個過載的函式,c 利用一種叫name mangling的技術,經過編譯器處理後,實...

c 之函式過載 函式匹配

case void f void f int void f int,int void f double,double 3.14 匹配原則 1 其形參數量與本次呼叫提供的實參數量相等 2 每個實參的型別與對應的形參型別相同,或者可以轉換成形參的型別 尋找最佳匹配 1 該函式每個實參的匹配都不劣於其他可...