摘要訊息 : 再次理解 C++ 11 引入的 constexpr.

0. 前言

C++ 11 中引入的所有特性中, 最讓人困惑的也許是 constexpr. 在《C++ 學習筆記》中, 我們詳細介紹過 constexpr, 這篇文章旨在幫助閣下深入理解 constexpr. 如果閣下一點都不了解 constexpr, 可以先閱讀《C++ 學習筆記》.

本文在 2022 年 5 月 16 日进行一次更新和修正. 修正之后本文已经归档, 不再享受更新.

1. 細談 constexpr

針對變數標識的 constexpr 表示該變數必定是編碼期就已知的變數, 也就是所謂的字面值常數表達式; 而針對函式標識的 constexpr 表明該函式有編碼期處理的能力, 但是函式的運作不一定就非要在編碼期, 也可以在運作期. 這個總結透露出了一個重要的信息, 就是 constexpr 在變數上和在函式上的行為並不一致. 對於變數來說, 如果被 constexpr 標識, 那麼它必定是編碼期已知的, 那麼它可以被用於標識陣列大小, 用在樣板引數上; 但是對於函式來說, 即使它被 constexpr 標識, 它也不是非要運作在編碼期, 而是可以在運作期執行. 因此, 我們要講語境分成兩個 : 編碼期語境和運作期語境.

constexpr 標識的函式, 如果存在某條陳述式在編碼期無法運作 (比如其呼叫的函式非 constexpr 函式或者某個變數在編碼期未知), 那麼整個函式會被推遲到運作期才運作; 否則, 函式的運作結果會在編碼期就產生. 相比較於 inline, 編碼器在處理 constexpr 函式的時候, 會盡力使得其可以在編碼期就產生結果; 但是對於 inline 來說, 編碼器並不盡力, 你的 inline 請求可能會被編碼器忽略. 至於某個被 constexpr 標識的函式是否產生了編碼器就可以得到的結果, 可以將其的回傳值放入到樣板引數或者陣列宣告中. 如果沒有產生編碼錯誤, 那麼結果就是在編碼期就產生; 否則, 函式需要等到運作期才會執行.

如果沒有 constexpr, 如果程式的某個地方要求編碼期語境的運作結果, 那麼此時我們還可能需要借助 template 來撰寫一個超函式.

在 C++ 11 中, 由于 constexpr 的严格限制, 大部分被 constexpr 標識的函式幾乎都可以在編碼期處理完成. 但是在 C++ 14 之後, constexpr 函式的限制被大大地放開, 大部分函式都可以被 constexpr 標識. 而在 C++ 20 之後, constexpr 的限制又被放開, 幾乎所有函式都可以被 constexpr 標識.