常量表示式的值需要在編譯時就得到計算,因此對宣告constexpr時用到的型別必須有所限制。因為這些型別一般比較簡單,值也顯而易見、容易得到,就把它們稱為「字面值型別」(literal type)。
算術型別、引用和指標都屬於字面值型別。某些類也是字面值型別,它們可能含有constexpr函式成員。自定義類sales_item、io庫、string型別不屬於字面值型別。
儘管指標和引用可以定義成constexpr,但它們的初始值受到嚴格限制。乙個constexpr指標的初始值必須是nullptr、0或儲存於某個固定位址中的物件。
函式體內定義的變數一般來說並非存放在固定位址中,因此constexpr指標不能指向這樣的變數。定義於函式體外的物件其位址固定不變,能用來初始化constexpr指標。允許函式定義一類有效範圍超出函式本身的變數,如區域性靜態變數,這類變數和定義在函式體之外的變數一樣有固定位址,因此constexpr引用能繫結到這樣的變數上,constexpr指標也能指向這樣的變數。
聚合類
聚合類(aggregate class)允許利用者直接訪問其成員,並且具有特殊的初始化形式。聚合類滿足以下條件:
所有成員都是public的
沒有定義建構函式
沒有類內初始值
沒有基類,也沒有虛函式
怎麼理解呢?
首先,看來看去聚合類其實就是乙個c結構體;其次,聚合這個詞,應該是相對組合的,表明了成員和類之間的鬆散關係。
當乙個類是聚合類時,就可以使用初始值列表像下面這樣初始化了:
struct point;
point pt = ;
字面值常量類
前面講過constexpr函式,它的引數和返回值都必須是常量表示式。而常量表示式的最基本要素就是字面值型別。字面值型別除了包括算數型別,引用和指標以外,某些類也屬於字面值型別,c++11稱之為字面值常量類。主要包括兩種情況:
首先資料成員都是字面型別的聚合類就是一種。上面的point類就是乙個例子。我們可以這樣理解,字面值的聚合繼續具有字面值的特徵,這裡主要是編譯階段可以求值。
還有一種情況,雖然不是聚合類,但是只要滿足下面的條件,也是字面值常量類:
資料成員必須都必須是字面值型別。
類必須至少含有乙個constexpr建構函式。
如果乙個資料成員含有類內初始值,則初始值必須是常量表示式;如果成員屬於某種類,初始值必須使用該類的constexpr建構函式。
類必須使用析構函式的預設定義。
對於這幾個條件,作者這樣理解:
滿足條件1,就可以在編譯階段求值,這一點和聚合類一樣。
滿足條件2,就可以建立這個類的constexpr型別的物件。
滿足條件3,就可以保證即使有類內初始化,也可以在編譯階段解決。
滿足條件4,就可以保證析構函式沒有不能預期的操作。
constexpr建構函式
通過前置constexpr關鍵字,就可以宣告constexpr建構函式,同時:
除了宣告為=default或者=delete以外,constexpr建構函式的函式體一般為空,使用初始化列表或者其他的constexpr建構函式初始化所有資料成員。
struct point
constexpr point()
:point(0,0){}
int x;
int y;
};constexpr point pt = ;
這樣宣告以後,就可以在使用constexpr表示式或者constexpr函式的地方使用字面值常量類了。
關鍵字 constexpr 於 c++11 中引入並於 c++14 中得到改善。它表示常數表示式。與 const 相同,它可應用於變數,因此如果任何**試圖修改該值,均將引發編譯器錯誤。與 const 不同,constexpr 也可應用於函式和類建構函式。 constexpr 指示值或返回值是常數,並且如果可能,將在編譯時計算值或返回值。
constexpr float x = 42.0;
constexpr float y;
constexpr float z = exp(5, 3);
constexpr int i; // error! not initialized
int j = 0;
constexpr int k = j + 1; //error! j not a constant expression
constexpr int sz = size(); //重要!!!只有當size是乙個constexpr函式時,才是一條正確的宣告語句
一般來說,如果你認定變數是乙個常量表示式,那就把它宣告成為constexpr
型別。
constexpr 函式是在使用需要它的**時,可以在編譯時計算其返回值的函式。當其引數為 constexpr 值並且在編譯時使用**需要返回值時(例如,初始化乙個 constexpr 變數或提供乙個非型別模板引數),它會生成編譯時常量。使用非constexpr 引數呼叫時,或編譯時不需要其值時,它將與正則函式一樣,在執行時生成乙個值。
遵循以下規定:(1)函式的返回型別以及所有形參的型別都得是字面值型別;(2)函式體中必須只有一條return語句。
#include using namespace std;
// c++98/03
templatestruct factorial
;template<> struct factorial<0>
;// c++11
constexpr int factorial(int n)
// c++14
constexpr int factorial2(int n)
int main()
**說明:
以上**演示了如何在編譯期計算3的階乘。
在c++11之前,在編譯期進行數值計算必須使用模板元程式設計技巧。具體來說我們通常需要定義乙個內含編譯期常量value的類模板(也稱作元函式)。這個類模板的定義至少需要分成兩部分,分別用於處理一般情況和特殊情況。
**示例中factorial元函式的定義分為兩部分:
當模板引數大於0時,利用公式 n!=n*(n-1)! 遞迴呼叫自身來計算value的值。
當模板引數為0時,將value設為1這個特殊情況下的值。
在c++11之後,編譯期的數值計算可以通過使用constexpr宣告並定義編譯期函式來進行。相對於模板元程式設計,使用constexpr函式更貼近普通的c++程式,計算過程顯得更為直接,意圖也更明顯。
但在c++11中constexpr函式所受到的限制較多,比如函式體通常只有一句return語句,函式體內既不能宣告變數,也不能使用for語句之類的常規控制流語句。
如factorial函式所示,使用c++11在編譯期計算階乘仍然需要利用遞迴技巧。
c++14解除了對constexpr函式的大部分限制。在c++14的constexpr函式體內我們既可以宣告變數,也可以使用goto和try之外大部分的控制流語句。
如factorial2函式所示,使用c++14在編譯期計算階乘只需利用for語句進行常規計算即可。
雖說constexpr函式所定義的是編譯期的函式,但實際上在執行期constexpr函式也能被呼叫。事實上,如果使用編譯期常量引數呼叫constexpr函式,我們就能夠在編譯期得到運算結果;而如果使用執行期變數引數呼叫constexpr函式,那麼在執行期我們同樣也能得到運算結果。
**第32行所演示的是在執行期使用變數n呼叫constexpr函式的結果。
準確的說,constexpr函式是一種在編譯期和執行期都能被呼叫並執行的函式。出於constexpr函式的這個特點,在c++11之後進行數值計算時,無論在編譯期還是執行期我們都可以統一用一套**來實現。編譯期和執行期在數值計算這點上得到了部分統一。
constexpr和指標
還記得const與指標的規則嗎?如果關鍵字const出現在星號左邊,表示被指物是常量;如果出現在星號右邊,表示指標本身是常量;如果出現在星號兩邊,表示被指物和指標兩者都是常量。
與const不同,在constexpr宣告中如果定義了乙個指標,限定符constexpr僅對指標有效,與指標所指物件無關。
const int *p = 0; // non-const pointer, const data
constexpr int *q = 0; // const pointer, non-const data
與其它常量指標類似,const
指標既可以指向常量也可以指向乙個非常量:
int j = 0;
constexpr int i = 2;
constexpr const int *p = &i; // const pointer, const data
constexpr int *p1 = &j; // const pointer, non-const data
參考: C constexpr型別說明符
本系列文章由 yhl leo 關鍵字constexpr於 c 11 中引入並於 c 14 中得到改善。它表示常數表示式。與const相同,它可應用於變數,因此如果任何 試圖修改該值,均將引發編譯器錯誤。與const不同,constexpr也可應用於函式和類建構函式。constexpr指示值或返回值是...
auto型別說明符
c 11新標準引入了auto型別說明符 用它能讓編譯器替我們分析表示式所屬的型別 auto讓編譯器通過初始值來推算出標量的型別。顯然auto定義的變數必須有初始值 auto item vall val2 item的型別通過val2和val1的相加的結果得出 使用auto也能在一條語句中宣告多個變數 ...
auto型別說明符
程式設計時,需要把表示式的值賦給變數,這就要求在宣告變數時清楚知道表示式的型別。為了解決這個問題,c 11引入auto型別說明符,用它就能讓編譯器替我們去分析表示式所屬的型別。auto讓編譯器通過初值來推算變數的型別。因此,auto定義的變數必須有初始值。auto能在一句中定義多個變數,但是這幾個變...