天天看點

C++11中的constexpr

一、為什麼要用constexpr

有時我們需要在程式編譯時就能得到某個對象或表達式的值,例如定義數組時指定數組大小的值。我們把這種表達式稱為常量表達式。

字面值屬于常亮表達式,用常亮表達式初始化的const對象也是常量表達式。是以我們說一個對象(或表達式)是不是常亮表達式由它的資料類型和初始值共同決定。這裡舉兩個反例:

string i="hello";                       //初始值是常量表達式但資料類型不是常量類型

const int j=get_size();               //資料類型是常量類型但初始值不是常量表達式

但是在一個複雜系統中,很難(幾乎肯定不可能)分辨一個初始值到底是不是常量表達式。其次如果要定義一個常量表達式對象,即使我們讓它的資料類型為const的,也不能完全保證這個對象就是常量表達式。

是以迫切需要一種解決此問題的方法。

二、constexpr的使用規則

1、C++11新标準規定,允許将變量聲明為constexpr類型以便由編譯器來驗證變量的值是否是一個常量表達式。聲明為constexpr的變量一定是一個常量,而且必須用常量表達式初始化,否則編譯器将報錯。

constexpr int i=20;                          //20是常量表達式

constexpr int j=i+1;                         //i+1是常量表達式

constexpr int k=size();                    //當size是個constexpr函數時,才是正确的聲明語句

2、常量表達式的值需要在編譯時就得到計算,是以對聲明constexpr時用到的類型必須有所限制。因為這些類型一般比較簡單,值也顯而易見、容易得到,就把它們稱為“字面值類型”。

   目前接觸過的類型中,算數類型、引用和指針和某些類都屬于字面值類型。自定義類、IO庫、string類型則不屬于字面值類型,也就不能被定義成constexpr。

3、盡管指針和引用都能定義成constexpr,但是它們的初始值卻受到嚴格限制。一個constexpr指針必須是nullptr或0,或者是存儲于某個固定位址中的對象。

   函數體内定義的變量一般來說并非存放在固定位址中,是以constexpr指針不能指向這樣的變量。定義于所有函數體之外的對象或者局部的靜态變量其位址固定不變,能用來初始化constexpr指針。

4、constexpr聲明中如果定義了一個指針,限定符constexpr僅僅對指針有效,與指針所指的對象無關。constexpr把它所定義的對象置為了頂層const。

const int *p=nullptr;                 //p是指針常亮

int *const r=nullptr;                 //r是常亮指針

constexpr int *q=nullptr;          //q是常亮指針

5、constexpr函數

   5.1、constexpr函數是指能用于常量表達式的函數。定義constexpr函數的方法與其它函數類似,不過要遵守幾項約定:函數的傳回類型及所有形參的類型都是字面值類型,而且函數體中必須有且隻有一條return語句。

constexpr int i(){return 0;}

constexpr int foo=i();

   5.2、當把constexpr函數用于初始化操作時,編譯器把對constexpr函數的調用替換成其結果值。為了能在編譯過程中随時展開,constexpr函數被隐式地指定為内聯函數,是以可以在程式中多次定義,且多次定義必須完全一緻,故常定義在頭檔案中。

   5.3、constexpr函數體内也可以包含其他語句,隻要這些語句在運作時不執行任何操作即可。例如空語句、類型别名以及using聲明。

   5.4、允許constexpr函數傳回非常量值,有時它的傳回值可以依賴于函數實參,即實參是常量表達式時,傳回值也是常量表達式;反之,當實參不是常量表達式時,其傳回值也就不是常量表達式。

   雖然constexpr函數允許傳回非常量表達式的值,但是當把該函數用在需要常量表達式的上下文中時,編譯器會檢查函數結果是否符合要求,若不符合将提示錯誤。

6、字面值常量類中的constexpr構造函數

參見——構造函數第六部分

繼續閱讀