C++ 從基礎到精通 — 第 17 講
使用 constexpr 與 consteval 進行編譯期計算
本部落格文章以教學為目的撰寫。 內容可能有誤,請搭配目標編譯器與標準函式庫文件確認。
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 大小。只在規則真正固定且提早驗證值得付出成本時使用。