← SEARCHABLE ARCHIVE
C / C++ · 2026-08-11

C++ 從基礎到精通 — 第 17 講

使用 constexpr 與 consteval 進行編譯期計算

INTERACTIVE READING MODE →

本部落格文章以教學為目的撰寫。 內容可能有誤,請搭配目標編譯器與標準函式庫文件確認。

C++ 能在程式啟動前完成部分計算。constexpr、consteval 與 constinit 分別表達可常數求值、必須在編譯期呼叫,以及靜態初始化。

constexpr 可在兩種環境執行

constexpr int Square(int value)
{
    return value * value;
}

constexpr int tableSize = Square(8); // 編譯期
int input = ReadInput();
int result = Square(input);          // 執行期

constexpr 函式可以接受常數求值,但並非每次呼叫都必須在編譯期執行。當引數為常數且結果用於需要 constant expression 的位置時,編譯器必須先算出結果。

consteval 強制編譯期呼叫

consteval unsigned int HashLiteral(std::string_view text)
{
    unsigned int hash = 2166136261u;
    for (char ch : text)
    {
        hash = (hash ^ static_cast<unsigned char>(ch)) * 16777619u;
    }
    return hash;
}

constexpr auto playerId = HashLiteral("Player");

consteval 的每次呼叫都必須是 constant expression,適合在建置時固定的資產鍵或通訊協定標籤。

雜湊仍可能碰撞。編譯期計算不保證唯一性,因此仍需碰撞政策與原始字串驗證。

用 if constexpr 移除不適用分支

template <typename T>
void Serialize(const T& value, Writer& writer)
{
    if constexpr (std::integral<T>)
        writer.WriteInteger(value);
    else
        value.Serialize(writer);
}

與一般 if 不同,未選中的 if constexpr 分支會從該模板實例中捨棄。只要不被選取,另一分支中不存在於該型別的操作也不必成立。

constinit 檢查靜態初始化

constinit int globalLimit = 64;

constinit 確認 static storage duration 物件能進行靜態初始化,但不會讓物件不可修改;不可修改是 const 的契約。

編譯期計算能減少執行期初始化並提早暴露錯誤,但會增加建置時間、診斷複雜度,甚至 binary 大小。只在規則真正固定且提早驗證值得付出成本時使用。