【轉】constexpr變數
常量表達式
常量表達式(const expression)是指值不會改變並且在編譯過程就能得到計算結果的表示式。顯然,字面值屬於常量表達式,用常量表達式初始化的const物件也是常量表達式。
一個物件(或表示式)是不是常量表達式由它的資料型別和初始值共同決定,例如:
1 const int max_files = 20; //max_files是常量表達式 2 const int limit = max_files + 1; //limit是常量表達式 3 int staff_size = 27; //staff_size不是常量表達式 4 const int sz = get_size(); //sz不是常量表達式
儘管staff_size的初始值是個字面值常量,但由於它的資料型別只是一個普通int而非const_int,所以它不屬於常量表達式。另一方面,儘管sz本身是一個常量,但它的具體值直到執行時才能獲取,所以也不是常量表達式。
constexpr變數
在一個複雜系統中,很難(幾乎肯定不能)分辨一個初始值到底是不是常量表達式。
constexpr是C++11開始提出的關鍵字,其意義與14版本有一些區別。
C++11中的constexpr指定的函式返回值和引數必須保證是字面值型別(literal type,常用的算術型別、引用和指標都屬於字面值型別)。而且必須有且只有一行return程式碼,這給函式的設計者帶來了更多的限制,比如通常只能通過return 三目運算子 + 遞迴來計算返回的字面值。
而C++14中只要保證返回值和引數是字面值就行了,函式體中可以加入更多的語句,方便更靈活的運算。
很多人都把constexpr和const相比較。
其實,const並不能代表“常量”,它僅僅是對變數的一個修飾,告訴編譯器這個變數只能被初始化,且不能被直接修改(實際上可以通過堆疊溢位等方式修改)。而這個變數的值,可以在執行時也可以在編譯時指定。
constexpr可以用來修飾變數、函式、建構函式。一旦以上任何元素被constexpr修飾,那麼等於說是告訴編譯器 “請大膽地將我看成編譯時就能得出常量值的表示式去優化我”。
如:
1 const int func() { 2 return10; 3 } 4 void main(){ 5 int arr[func()]; 6 }
對於func() ,膽小的編譯器並沒有足夠的膽量去做編譯期優化,哪怕函式體就一句return 字面值;
1 constexpr int func() { 2 return 10; 3 } 4 void main(){ 5 int arr[func()]; 6 }
編譯期大膽地將func()做了優化,在編譯期就確定了func計算出的值10而無需等到執行時再去計算。
這就是constexpr的第一個作用:給編譯器足夠的信心在編譯期去做被constexpr修飾的表示式的優化。
constexpr還有另外一個特性,雖然它本身的作用之一就是希望程式設計師能給編譯器做優化的信心,但它卻猜到了自己可能會被程式設計師欺騙,而編譯器並不會對此“惱羞成怒”中止編譯,如:
1 constexpr int func(const int n){ 2 return 10+n; 3 } 4 void main(){ 5 const int i = cin.get(); 6 cout<<func(i); 7 }程式設計師告訴編譯器儘管信心十足地把func當做是編譯期就能計算出值的程式,但卻欺騙了它,程式設計師最終並沒有傳遞一個常量字面值到該函式。沒有被編譯器中止編譯並報錯的原因在於編譯器並沒有100%相信程式設計師,當其檢測到func的引數是一個常量字面值的時候,編譯器才會去對其做優化,否則,依然會將計算任務留給執行時。
基於這個特性,constexpr還可以被用來實現編譯期的type traits,比如STL中的is_const的完整實現:
1 template<class _Ty, 2 _Ty _Val> 3 struct integral_constant 4 { // convenient template for integral constant types 5 static constexpr _Ty value = _Val; 6 7 typedef _Ty value_type; 8 typedef integral_constant<_Ty, _Val> type; 9 10 constexpr operator value_type() const noexcept 11 { // return stored value 12 return (value); 13 } 14 15 constexpr value_type operator()() const noexcept 16 { // return stored value 17 return (value); 18 } 19 }; 20 21 typedef integral_constant<bool, true> true_type; 22 typedef integral_constant<bool, false> false_type; 23 24 template<class _Ty> 25 struct is_const 26 : false_type 27 { // determine whether _Ty is const qualified 28 }; 29 30 template<class _Ty> 31 struct is_const<const _Ty> 32 : true_type 33 { // determine whether _Ty is const qualified 34 }; 35 int main() { 36 37 static_assert(is_const<int>::value,"error");//error 38 static_assert(is_const<const int>::value, "error");//ok 39 return 0; 40 }