constexpr、consteval 與 constinit(編譯期求值)
有些計算根本不必在程式執行時發生,因為它們的輸入在你編譯時就已知——一張查找表、一個緩衝區的大小、一個雜湊過的字串字面值。若你能把那份工作推進編譯器裡,執行中的程式就只是讀取一個預先算好的答案,在執行期不付任何代價。這三個關鍵字是 C++ 用來把計算與初始化從執行期移到編譯期的控制鈕。
constexpr 意思是「若輸入為常數,這可以在編譯期求值」。constexpr 函式以編譯期已知的引數呼叫時可在編譯時執行(產生一個烤進二進位檔的常數),但以執行期值呼叫時也可以在執行期正常執行——它是許可,不是命令。consteval 更強:它標記一個即時函式(immediate function),必須在編譯期求值,每次呼叫皆然,完全沒有執行期退路;若你無法在編譯期求值它,程式就不會編譯。constinit 是格格不入的那一個——它不是關於算出某個值,而是關於變數何時被初始化:它要求靜態或全域變數在編譯期/常數初始化期間被賦予其值,藉此排除惡名昭彰的「靜態初始化順序慘案」與任何隱藏的執行期啟動工作,不過該變數之後仍可被更改(它不是 const)。
為何重要:編譯期求值是零開銷原則的乾淨展現——你用較慢的建構換取較快、較小、較可預測的執行。它大量用於查找表、驗證與後設程式設計,而現代 C++ 讓出奇豐富的程式碼(迴圈、配置,甚至某些容器)能在編譯期執行。誠實的限制:常數求值情境裡並非什麼都允許(不得有未定義行為、不得呼叫非 constexpr 函式、對可配置之物有限制),constexpr 函式除非情境強制否則不保證在編譯期執行,而 constinit 只約束初始化時機——它不會讓變數變成不可變。
constexpr int sq(int n) { return n * n; } constexpr int k = sq(8); // 64 在編譯期算出;char buf[k]; 於是是固定大小的陣列
因為 sq(8) 有常數引數且 constexpr 允許它,值 64 被烤進二進位檔——零執行期工作。
constexpr 是「允許在編譯期求值」,不是保證:用執行期值呼叫 constexpr 函式,它就像任何函式一樣在執行期執行。當你必須禁止執行期路徑時用 consteval,並記得 constinit 只固定初始化時機,不固定可變性。