摘要訊息 : C++ 14 提出了緩和對 constexpr
函式的要求.
0. 前言
C++ 11 引入了 constexpr
關鍵字, 用於標識一個變數或者函式. 對於變數來說, 它就是常數表達式; 對於函式來說, 編碼器有機會讓函式直接在編碼期完成計算. 但是 C++ 11 對於 constexpr
的限制實在是太嚴格了, 特別是對於 constexpr
函式.
C++ 14 的提案 N3652 《Relaxing constraints on constexpr functions》這篇提案旨在緩和這些約束, 讓 constexpr
函式在日常程式設計中更加常見.
更新紀錄 :
- 2022 年 4 月 16 日進行第一次更新和修正.
1. 提案內容
在 C++ 11 中, 一個具有 constexpr
標識的函式只允許出現
- 空的陳述式;
static_assert
;typedef
以及using
, 但是不能使用它們定義一個類別或者列舉;- 在非建構子中, 必須有一個
return
陳述式.
N3652 提出, 允許下面內容出現在函式中 :
- 允許
constexpr
函式中宣告變數, 但是變數不可以是未初始化的,static
的和thread_local
的; - 允許
constexpr
函式中存在條件陳述式if
以及switch
; - 允許
constexpr
函式中存在迴圈for
(包含 Range-For),while
以及do-while
; - 允許生命週期在常數表達式計算之內的變數在
constexpr
函式中發生改變; - 允許
constexpr
函式的回傳值為void
, 並且函式中可以不出現return
陳述式 (void
在 C++ 11 中非字面值型別, 但是在 C++ 14 中成為字面值型別).
constexpr void func() noexcept {
if(1 == 2) {
while(1 < 2) {
switch(false) {
default:
break;
}
}
}
int arr[] {1, 2, 3, 4, 5, 6, 7};
arr[2] = 10;
for(auto &value : arr) {
do {
++value;
}while(false);
}
}
在 Code 1 中的每一行程式碼都只能在 C++ 14 做到, 包括其不存在 return
陳述式.
儘管 C++ 14 放鬆了對於 constexpr
函式的約束, 但是對於 constexpr
函式仍然還是要遵循以下的限制 :
- 函式不可以是虛擬函式;
- 回傳值對應的型別必須是一個字面值型別;
- 每一個參數對應的型別都屬於字面值型別;
- 函式中不能出現
__asm
,__asm__
,static
,thread_local
,goto
以及try
-catch
區塊.
除此之外, 類別的建構子如果要被 constexpr
標識, 那麼在 C++ 14 中還需要遵守下面的限制 :
- 類別不可以擁有虛擬基礎類別;
- 每一個非
volatile
非靜態成員變數和基礎類別中的物件都需要被初始化; - 如果類別是一個非空的
union
類別或者包含不具名非空等位成員, 那麼每一個非靜態成員變數都應該被初始化; - 每一個非靜態成員以及基礎類別物件的初始化所使用的建構子也應該被
constexpr
所標識.
2. 總結
放鬆了 constexpr
對於函式的約束, 這將帶來非常多的好處. 例如, 我們可以直接對符合條件的函式標識 constexpr
, 讓編碼器幫我們判斷這個函式是否具有編碼期計算的能力. 有機會的話, 直接就可以在編碼器得到函式呼叫的結果, 提高了程式的效能. 在 C++ 11 中, 如果我們想那些函式在編碼期計算, 可能必須使用 Template Meta-Programming.
自創文章, 原著 : Jonny. 如若閣下需要轉發, 在已經授權的情況下請註明本文出處 :