一、constexpr意義
將變量聲明為constexpr類型以便由編譯器來驗證變量是否是一個常量表達式(不會改變,在編譯過程中就能得到計算結果的表達式)。是一種比const更強的約束,這樣可以得到更好的效率和安全性。
二、constexpr用法
1.修飾函數
/*1.如果size在編譯時能確定,那么返回值就可以是constexpr,編譯通過*/ constexpr int getSizeA(int size) { return 4*size; } /*2.編譯通過,有告警:在constexpr中定義變量*/ constexpr int getSizeB(int size) { int index = 0; return 4; } /*3.編譯通過,有告警:在constexpr中定義變量(這個有點迷糊)*/ constexpr int getSizeC(int size) { constexpr int index = 0; return 4; } /*4.編譯通過,有告警:使用了if語句(使用switch也會告警)*/ constexpr int getSizeD(int size) { if(0) {} return 4; } /*5.定義變量並且沒有初始化,編譯不過*/ constexpr int getSizeE(int size) { int index; return 4; } /*6.rand()為運行期函數,不能在編譯期確定,編譯不過*/ constexpr int getSizeF(int size) { return 4*rand(); } /*7.使用了for,編譯不過*/ constexpr int getSizeG(int size) { for(;0;) {} return 4*rand(); }
總結:constexpr修飾的函數,不能依賴任何運行期的信息,不要定義任何變量常量,並且必須盡量簡單,要不就會編譯不過或告警(坑)。
2.修飾類型
int tempA; cin>>tempA; const int ctempA = 4; const int ctempB = tempA; /*1.可以再編譯器確定,編譯通過*/ constexpr int conexprA = 4; constexpr int conexprB = conexprA + 1; constexpr int conexprC = getSizeA(conexprA); constexpr int conexprD = ctempA; /*2.不能在編譯期決定,編譯不過*/ constexpr int conexprE = tempA; constexpr int conexprF = ctempB;
總結:constexpr修飾的常量必須在編譯期確定值,上面的例子也體現出了和const之間的差別。const既可以在編譯期確定如ctempA,也可以在運行期確定如ctempB,使用范圍更廣。還有一點constexpr只能修飾字面值類型如算數類型、引用類型、指針以及后面介紹的字面值常量類。
3.修飾指針
int g_tempA = 4; const int g_conTempA = 4; constexpr int g_conexprTempA = 4; int main(void) { int tempA = 4; const int conTempA = 4; constexpr int conexprTempA = 4; /*1.正常運行,編譯通過*/ const int *conptrA = &tempA; const int *conptrB = &conTempA; const int *conptrC = &conexprTempA; /*2.局部變量的地址要運行時才能確認,故不能在編譯期決定,編譯不過*/ constexpr int *conexprPtrA = &tempA; constexpr int *conexprPtrB = &conTempA constexpr int *conexprPtrC = &conexprTempA; /*3.第一個通過,后面兩個不過,因為constexpr int *所限定的是指針是常量,故不能將常量的地址賦給頂層const*/ constexpr int *conexprPtrD = &g_tempA; constexpr int *conexprPtrE = &g_conTempA constexpr int *conexprPtrF = &g_conexprTempA; /*4.局部變量的地址要運行時才能確認,故不能在編譯期決定,編譯不過*/ constexpr const int *conexprConPtrA = &tempA; constexpr const int *conexprConPtrB = &conTempA; constexpr const int *conexprConPtrC = &conexprTempA; /*5.正常運行,編譯通過*/ constexpr const int *conexprConPtrD = &g_tempA; constexpr const int *conexprConPtrE = &g_conTempA; constexpr const int *conexprConPtrF = &g_conexprTempA; return 0; }
總結:constexpr指針不能用局部變量賦值,const指針可以;constexpr指針里是頂層const,即指針是常量,而不是所指向的類型是常量,如果要指向的類型也為常量,要用constexpr const來修飾。
4.修飾引用
int g_tempA = 4; const int g_conTempA = 4; constexpr int g_conexprTempA = 4; int main(void) { int tempA = 4; const int conTempA = 4; constexpr int conexprTempA = 4; /*1.正常運行,編譯通過*/ const int &conptrA = tempA; const int &conptrB = conTempA; const int &conptrC = conexprTempA; /*2.有兩個問題:一是引用到局部變量,不能再編譯器確定;二是conexprPtrB和conexprPtrC應該為constexpr const類型,編譯不過*/ constexpr int &conexprPtrA = tempA; constexpr int &conexprPtrB = conTempA constexpr int &conexprPtrC = conexprTempA; /*3.第一個編譯通過,后兩個不通過,原因是因為conexprPtrE和conexprPtrF應該為constexpr const類型*/ constexpr int &conexprPtrD = g_tempA; constexpr int &conexprPtrE = g_conTempA; constexpr int &conexprPtrF = g_conexprTempA; /*4.正常運行,編譯通過*/ constexpr const int &conexprConPtrD = g_tempA; constexpr const int &conexprConPtrE = g_conTempA; constexpr const int &conexprConPtrF = g_conexprTempA; return 0; }
總結:簡單的說constexpr所引用的對象必須在編譯期就決定地址。還有一個奇葩的地方就是可以通過上例conexprPtrD來修改g_tempA的值,也就是說constexpr修飾的引用不是常量,如果要確保其實常量引用需要constexpr const來修飾。
5.修飾類(未完待續)