C++中的默認參數規則

C++中的默認參數規則

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中是不被容許的,這個範圍還挺廣。對象

  1. 局部變量不能在默認參數中
  2. this指針不能在默認參數中
  3. 其餘的參數不能在默認參數中
  4. 非靜態成員不能在默認參數中
  5. 有捕獲內容的lambda表達式不能在默認參數中
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會同時把默認參數導入進來。

其它的小規則

默認參數還有一些其它的小規則。

在類外定義的函數,能夠將其默認參數與聲明組合,但不能將成員函數變成構造函數。

虛函數的默認參數由靜態類型決定。

除了調用運算符之外,其它的運算符都不能有默認參數。

對於在不一樣翻譯單元內定義的內聯函數,那麼在每一個翻譯單元末尾,默認參數集都須要相同。

友元函數聲明若是有默認參數,那麼這個聲明必須是定義而且在這個翻譯單元中不會再有別的聲明

相關文章
相關標籤/搜索