後端工坊 2026 年 10 月 11 日

2026-10-11 — C++26 收掉四類 UB:改成編譯錯誤或有定義行為

primary=https://www.sandordargo.com/blog/2026/07/29/cpp26-reduces-undefined-behaviour primary=https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3144r2.pdf primary=https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2795r5.html primary=https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3471r4.html primary=https://isocpp.org/blog/2026/10/cpp26-reducing-undefined-behaviour-sandor-dargo

C++26 收掉四類 UB:改成編譯錯誤或有定義行為

Sandor Dargo(isocpp.org 於 2026-10-09 轉載) · 2026-10-09

C++26 把四類原本「編譯過了、跑起來才知道」的未定義行為(UB),改成編譯期錯誤、有定義的錯誤行為,或可在執行期攔截的契約違反。受影響的不是新語法,而是你現有程式碼的語意:升級編譯器與標準版本後,有些舊碼會直接編譯失敗,有些則從「隨機壞掉」變成「可診斷」。

這四項分別是 P3144R2、P2795R5、P2748R5 與 P3471R4。

原本的問題

UB 的麻煩在於編譯器不必告訴你。對不完整型別(incomplete type)做 delete、讀取未初始化的區域變數、回傳綁到暫時物件的參考,這些寫法在舊標準下都是 UB,程式可能正常、可能損毀,換個優化等級結果就不同。

另一類問題在標準函式庫:vector 越界存取、對空的 optional 解參考,違反前置條件時標準只說「行為未定義」,沒有統一的方式要求實作去檢查。

核心改動

一、對不完整型別 delete 變成 ill-formed。舊標準只在「完整類別的解構函式是 trivial、且沒有類別專屬 deallocation function」時才不算 UB;P3144R2 之後直接編譯失敗。

C++23 以前C++26
delete 不完整型別指標UB(特定條件除外)ill-formed,編譯器拒絕
讀取未初始化區域變數UB錯誤行為(erroneous behaviour)
回傳參考綁到暫時物件UBill-formed
函式庫前置條件違反UB硬化實作中為契約違反
struct Widget;              // 此編譯單元看不到定義
void destroy(Widget* p) {
    delete p;               // C++26: ill-formed
}

修法是在 delete 的位置讓型別完整,通常就是多 #include 一個標頭。文章指出主流編譯器過去就會對這種寫法發警告,所以多數程式碼早該乾淨。

二、未初始化讀取變成「錯誤行為」。P2795R5(日期 2024-03-22)新增這個行為類別,定義為「well-defined behavior that the implementation is recommended to diagnose」。它仍然是程式碼的錯,但不再有安全風險,物件會拿到由實作決定的值。

void f(int);
void g() {
  int x [[indeterminate]], y;
  f(y);     // erroneous behavior
  f(x);     // undefined behavior
}

真的需要保留未初始化儲存(例如大型暫存緩衝區)時,用 [[indeterminate]] 明確標註,此時讀取仍是 UB。這項改動只需重新編譯,但效能敏感的程式要檢查哪些緩衝區該加屬性。

其餘兩項

三、回傳參考綁暫時物件變成 ill-formed。P2748R5 讓 return 把回傳的參考綁到暫時物件時直接報錯。文章的例子包括從 42 回傳 auto&&,以及回傳 const double& 卻從 static int 隱含轉換出暫時物件;這兩種以前都編得過,現在不行。

四、函式庫硬化。P3471R4(日期 2025-02-14)讓部分前置條件違反在「硬化實作」中成為契約違反,且不能用 ignore 語意關掉。是否為硬化實作、怎麼開啟(編譯旗標或巨集)都由實作自行決定;非硬化實作下仍是 UB。

  • 涵蓋 span、basic_string_view、序列容器的 front/back/a[n]、basic_string
  • 也涵蓋 mdspan、bitset、valarray、optional 的 operator->/operator*,以及 expected
  • 論文引用 Google 的部署數據:效能影響低至 0.3%,找出超過 1000 個 bug

影響範圍

會直接編譯失敗的是前兩類 ill-formed 變更:用前置宣告加 unique_ptr 或裸指標、卻在沒看到定義的檔案裡銷毀物件的程式碼,以及回傳參考時依賴暫時物件轉換的輔助函式。升級到 C++26 的 CI 要先看這兩種錯誤。

函式庫硬化不改程式碼,但跑 release 的服務要決定是否開啟:它把越界從「偶發損毀」變成「確定終止」,代價是論文引用的那組低至 0.3% 的數字是否適用於你的負載,論文未保證。混用硬化與非硬化的編譯單元,論文規定為 unspecified,連結多個函式庫時要統一旗標。

原始來源:Sandor Dargo 原文、P3144R2、P2795R5、P3471R4、isocpp.org 轉載


End of article
0
Would love your thoughts, please comment.x
()
x