天天看點

C++知識整理系列(三)—— constexpr常量表達式一、常量表達式二、constexpr變量五、指針和constexpr六、字面值常量類參考

  • const修飾常量,但是const并未區分編譯時常量和運作時常量,而constexpr則隻能是編譯時常量,在C++11中提出。
  • 這篇文章,将詳細講解constexpr。

目錄

  • 一、常量表達式
  • 二、constexpr變量
    • 三、constexpr函數
    • 四、字面值類型
  • 五、指針和constexpr
  • 六、字面值常量類
  • 參考

一、常量表達式

常量表達式(const expression):指值不會改變并且在編譯階段過程就能得到計算結果的表達式。

以下兩種是常量表達式:

const int maxSize = 10;
const int limit = maxSize + 1;
           

以下兩種不是常量表達式:

int staff_size = 27;
const int sz = get_size();
           
  • staff_size的初始值雖然是個字面值常量,但它的資料類型隻是普通的int而非const int,還是可以被重新指派的,是以不是常量表達式。
  • sz本身是一個常量,但它的具體值直到運作時才能擷取到,是以也不是常量表達式。

二、constexpr變量

在一個複雜系統中,很難分辨一個初始值到底是不是常量表達式。從前面的例子可以發現,即使變量加上const,但是指派是在運作時确定的也不是常量表達式。

C++11新标準規定,允許将變量聲明為constexpr類型以便由編譯器來驗證變量的值是否是一個常量表達式。

  • 聲明為constexpr的變量一定是一個常量。
  • 必須用常量表達式初始化。
constexpr int mf = 20;					//20是常量表達式
constexpr int limit = mf + 1;			//mf + 1是常量表達式
constexpr int sz = size();				//隻有當size是一個constexpr函數時才是一條正确的聲明語句
           

size()函數也需要constexpr修飾,成為constexpr函數。

三、constexpr函數

constexpr函數指能用于常量表達式的函數。定義constexpr函數有幾項約定:

  • 函數的傳回值類型及所有的類型都得是字面值類型。
  • 函數體中必須有且隻有一條return語句。
constexpr int new_sz() { return 40; }
constexpr int foo = new_sz();		//正确:foo是一個常量表達式
           

因為編譯器能在程式編譯時驗證new_sz函數傳回的是常量表達式,是以可以用new_sz函數初始化constexpr類型的變量foo。

(1)執行初始化任務時,編譯器把對constexpr函數的調用替換成其結果值。為了能在編譯過程中随時展開,constexpr函數被隐式地指定為内聯函數。

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

(3)constexpr函數的傳回值可以不是一個常量:

//cnt如果是常量表達式,傳回值就是常量表達式
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
           

比如,下面兩個例子:

int arr[scale(2)];			//正确:scale(2)是常量表達式
int i = 3;
int a[scale(i)];			//錯誤:scale(i)不是常量表達式
           
  • 給scale傳入字面值為2的常量表達式時,它的傳回類型也是常量表達式。此時編譯器用對應的結果值(80)替換為對scale函數的調用。
  • 當我們用一個非常量表達式調用scale函數時,比如

    int i = 3

    的對象i,傳回值則不是一個常量表達式。當把scale函數用在需要常量表達式的上下文中時,編譯器發現不是常量表達式,發出錯誤資訊。

(4)constexpr函數通常定義在頭檔案中。因為編譯器要想展開函數不僅需要函數聲明還需要函數定義,而constexpr函數可以在程式中多次定義,但多個定義必須完全一緻。

四、字面值類型

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

字面值類型包括:算數類型、引用、指針,自定義類、string等類型不是字面值類型,也就不能定義成constexpr。

盡管指針和引用都能定義成constexpr,但它們的初始值卻受到嚴格限制。一個constexpr指針的初始值必須是

nullptr

或者

,或者是存儲在某個固定位址中的對象。

函數體内定義的變量一般來說并非存放在固定位址中,是以constexpr指針不能指向這樣的變量。相反的,定義在函數體之外的對象位址固定不變,能用來初始化constexpr指針。

五、指針和constexpr

(1)如果在constexpr聲明中定義了一個指針,限定符constexpr僅對指針有效,與指針所指的對象無關。

const int *p = nullptr;					//p是一個指向整數常量的指針
constexpr int *q = nullptr;				//q是一個指向整數的常量指針
           

q是一個常量指針,因為constexpr把它所定義的對象置為了頂層const。類似于:

int *const q = nullptr;

(2)與其他常量指針類似,constexpr指針即可以指向常量也可以指向一個非常量:

constexpr int *np = nullptr;			//np是一個指向整數的常量指針,其值為空
int j = 0;
constexpr int i = 40;					//i的類型是整數常量
//假設i和j都定義在函數體之外
constexpr const int *p = &i;			//p是常量指針,指向整型常量i
constexpr int *p1 = &j;					//p1是常量指針,指向整數j
           

六、字面值常量類

constexpr函數的參數和傳回值必須是字面值類型。注意,函數的傳回值必須是字面值類型,但可以不是一個常量。

和其他類不同,字面值類型的類可能含有constexpr函數成員。這樣的成員必須符合constexpr函數的所有要求,它們是隐式const。

字面值常量類:資料成員都是字面值類型的聚合類。如果一個類不是聚合類,但它符合下述要求,則它也是一個字面值常量類:

  • 資料成員都必須是字面值類型。
  • 類必須至少含有一個constexpr構造函數。
  • 如果一個資料成員含有類内初始值,這内置類型成員的初始值必須是一條常量表達式;如果成員屬于某種類類型,這初始值必須使用成員自己的constexpr構造函數。
  • 類必須使用析構函數的預設定義,該成員負責銷毀類的對象。

盡管構造函數不能是const的,但是字面值常量類的構造函數可以是constexpr函數。一個字面值常量類必須至少提供一個constexpr構造函數。

參考

  • C++ Primer

碼字不易,覺得不錯的小夥伴可以一鍵三連支援一下~

C++知識整理系列(三)—— constexpr常量表達式一、常量表達式二、constexpr變量五、指針和constexpr六、字面值常量類參考

繼續閱讀