C++的默認參數規則實際上是一個很是容易掉坑的規則,尤爲是當一個函數擁有多個聲明的時候,每一個聲明的默認參數能夠各不相同,在調用時又可能與每一個聲明都不一樣;這篇博客稍微列舉一下C++中的默認參數規則。python
在開始以前,咱們先來複習一下,函數能夠有多個聲明,定義 是有函數體的聲明。默認參數則是函數聲明中使用特殊語法(decl-specifier-seq declarator = initializer
)爲某個參數提供的默認值。這種語法就像在參數列表中寫參數對象的拷貝初始化同樣。數組
void foo(int a, int b = 0); void func(int a = 1, int b = 2, int c = 3);
默認參數語法存在的目的,是爲用戶在函數調用時,能夠不提供尾隨參數。函數
func(); func(1); func(1,2);
很是容易理解,因爲C++目前尚未像python那樣指定參數的語法,於是須要提供默認值的參數必須放到參數列表的後面。實質上,這至關於編譯器替用戶向函數中傳遞參數。this
那麼,這裏就有這麼幾個問題,什麼地方能夠有默認參數?哪些東西能夠做爲默認參數?當函數有多個聲明時,默認參數如何工做?翻譯
到我開始寫這篇文章爲止,C++容許普通的函數聲明(包括類成員函數)、lambda表達式中使用默認參數,而不容許函數指針、函數引用以及typedef聲明中出現默認參數。具體可見這裏。因此,在須要管理各類包裝計算函數的對象的場景中(algorithm factory),因爲大部分手法使用函數指針進行,保存默認參數須要使用額外的空間並在運行時完成。指針
這裏能夠稍微留個問題,std::function支持默認參數嗎?若是不,爲何?code
從文法上說,全部能作initializer的東西都能做爲默認參數,但問題在於,有些東西出如今initializer中是不被容許的,這個範圍還挺廣。對象
int main(int argc = 0,char** argc = {0}); // ok int foo(int a1, int a2, int a3 = a1 + a2); // bad int func(int a1, int a2, int a3 = 1 + 2); // ok void helper() { int n = 1; int func(int a1 = n);//bad } //C is a class with copy constructor C::foo(C p = *this); // bad class C { static int s; int a; //void func(int n = a);//bad void func(int n = s);//OK } //after C++11 int globalV; void defaultFuncs(int a1 = ([]()->int {return 0; })(), int a2 = 1, int a3 = 3 + 2);//ok //void defaultFuncs(int a1 = ([]()->int {return globalV; })(), int a2 = 1, int a3 = 2);//bad
而且,因爲默認參數實質上就是編譯器替用戶填參數,而函數調用時會發生argument到parameter的拷貝初始化,於是這個initializer必需要可以知足到相應parameter的拷貝初始化。遊戲
除此以外,其它的東西均可以作默認參數。ci
若是每一個函數都只有一個聲明兼定義,那麼事情會簡單不少,可是,在C++中,一個函數能夠有多個聲明,但只能有一個定義,這和名稱查找規則共同協做,構成了C++的分離編譯特性。隨之而來的,默認參數在多個聲明之間有頗有趣的工做特性。
第一個特性,就是不一樣聲明之間的默認參數是能組合的。
void multi(int a1, int a2, int a3) { std::cout << a1 << a2 << a3 << '\n'; } void multi(int a1,int a2, int a3 = 3); void multi(int a1, int a2 = 2, int a3); void multi(int a1 = 1, int a2, int a3); int main() { multi();//ok ,call multi(1,2,3); }
這是一個很隱蔽的特性,隱蔽到筆者的VS2017intelliScene會對它報錯,然而編譯仍是能經過。事實上,標準中有這麼一句規定:
全部的有默認參數的形參後面,全部的形參都必須在這個聲明或者在先前的聲明提供默認參數,或者是參數包。
在函數調用點,實際上的默認參數是函數全部可見聲明的默認參數的聯合。可是相應的,在這個可見集合中,不能有對於同一個形參重複的默認參數聲明,即便是同一個值也不行。
int foo(int,int); int foo(int a1, int a2 = 0); int foo(int a1, int a2 = 0);//bad
簡單而言,內部做用域中能夠從新聲明一個函數,而且能夠忽視外部聲明的默認參數。在這個內部做用域中的函數調用,其默認參數集合也是這個這個做用域中全部聲明的默認參數聯合。固然,這僅僅是可見聲明這個概念,也就是名稱查找的小遊戲而已。
同理,若是你外部做用域與內部做用域均有默認參數定義,那麼using會同時把默認參數導入進來。
默認參數還有一些其它的小規則。
在類外定義的函數,能夠將其默認參數與聲明組合,但不能將成員函數變成構造函數。
虛函數的默認參數由靜態類型決定。
除了調用運算符之外,其它的運算符都不能有默認參數。
對於在不一樣翻譯單元內定義的內聯函數,那麼在每一個翻譯單元末尾,默認參數集都須要相同。
友元函數聲明若是有默認參數,那麼這個聲明必須是定義而且在這個翻譯單元中不會再有別的聲明