當我在尋找下一個文章主題時,我的目光停留在 std::is_within_lifetime 上。畢竟,處理生命週期問題是常見的錯誤來源。接著我點擊了連結,看到的是「檢查 union 替代成員是否活躍」的內容,我不禁皺眉,這連結正確嗎?

讓我們深入細節,先來看看 P2641R4 是什麼。

C++26 在 <type_traits> 標頭中新增了 bool std::is_within_lifetime(const T* p) 函式。此函式用於檢查指標 p 是否指向一個在編譯時計算期間仍處於生命週期內的物件。

最常見的使用情境是判斷 union 中哪個成員目前是活躍的。以下是一個簡單範例:

在此範例中,當賦值給 s.i 後,該成員即成為活躍成員。呼叫 std::is_within_lifetime(&s.i) 會回傳 true,確認 i 正在其生命週期內。如果此時檢查 std::is_within_lifetime(&s.d),則會回傳 false,因為 d 並非活躍成員。

此函式有一些有趣的設計選擇,值得討論。

std::is_within_lifetime 是 consteval 函式,意味著它只能在編譯時計算期間使用,無法在執行時呼叫。

這看似有限制,但其實是刻意設計。此函式的目的是解決編譯時計算中特有的問題。執行時你可以使用其他機制,例如用額外變數追蹤狀態。編譯器在執行時計算時不會維持與編譯時計算相同層級的生命週期追蹤資訊。

此函式接受指標而非參考,這對查詢操作來說可能不尋常。原因很簡單:傳參考可能會因暫時物件與生命週期延長規則而帶來複雜性。

使用指標明確表達意圖——你是在詢問特定記憶體位置,而非值或可能綁定到多種物件的參考。這對函式的實際功能來說語意更清晰。

你可能會好奇,為何此功能名稱如此通用,而主要使用場景卻是 union。答案是委員會選擇從更基礎的層面解決問題。

他們沒有新增專門針對 union 的檢查,而是提供一個通用機制來查詢物件生命週期。這表示 std::is_within_lifetime 未來可能在其他編譯時計算場景中也有用,當你需要知道物件是否存在時。

這種通用化讓此功能更強大且具備未來擴展性,即使目前主要用途是檢查 union 成員活躍狀態。

此提案源自一個非常具體的問題:如何以最小儲存空間實作 Optional<bool>。想像你想建立一個型別,可以持有布林值或為空,且盡可能節省記憶體。

在執行時,你可以用 c 中的哨兵值追蹤活躍成員,例如用 2 表示「無值」,因為布林值只用 0 或 1。但在編譯時計算時,這會變得棘手。編譯器需要知道哪個 union 成員活躍,不能依賴執行時技巧。

在 C++26 之前,根本沒有標準方法能在編譯時計算時檢查這點。有了 std::is_within_lifetime,解決方案變得簡單明瞭:

在編譯時計算期間,我們用 std::is_within_lifetime 檢查 b 是否為活躍成員。執行時則退回檢查哨兵值。這樣我們同時擁有編譯時正確性與執行時效率。

更新:正如 Mikhail Svetkin 在留言區指出,Clang 已經透過 __builtin_is_within_lifetime 內建函式支援此功能,libc++ 也已經將 std::is_within_lifetime 連結到該內建函式。因此 LLVM/Clang 工具鏈已經有早期支援。感謝 Mikhail!

C++26 的 std::is_within_lifetime 是一個專注的新增功能,解決了編譯時計算中真實存在的問題:在不引發未定義行為的情況下檢查 union 成員是否活躍。雖然最初動機來自於實作節省空間的 optional 型別,但委員會明智地選擇以更通用的方式處理底層問題。

此函式的設計——接受指標、僅限 consteval、名稱廣泛——反映了對當前需求與未來應用的深思熟慮。它是一個小巧但設計良好的元件,使 constexpr 評估更實用且表達力更強。

自從 C++11 引入 constexpr 後,其適用範圍逐步擴大。起初我們甚至無法使用 if、else 或迴圈,這些限制在 C++14 被解除。C++17 又加入了更多支援……