C++ 11 中引入的所有特性中, 最讓人困惑的也許是 constexpr. 我也曾經被它困惑了一段時間, 儘管我在《C++ 學習筆記》中講述地比較詳細, 不過我早已解決了這個困惑. 如今閱讀《Effective Modern C++》的過程中, 還是決定專門為 constexpr 寫一篇文章, 幫助大家更加深入地理解 constexpr. 這篇文章旨在幫你深入理解 constexpr, 如果你一點都不了解 constexpr, 我想你應該去看一下我那篇《C++ 學習筆記》, 了解一下先

在開始細談之前, 我首先來總結一下 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 標識. 我們已經在《Paper Guide》欄目中介紹過 C++ 14 中是如何緩和 constexpr 函式的限制的. 在 C++ 20, 幾乎任何函式都可以被 constexpr 標識, 我們也將在《Paper Guide》欄目中介紹 C++ 20 的 constexpr

C++ 的發展已經越來越致力於 everything constexpr. 其實個人覺得必要不是特別大, 因為 everything constexpr 可以作為一個語言的特點來宣傳, 而 C++ 連 Module、Coroutine、Network 和 Reflection 這些該有的還處於殘缺的狀態