合約斷言 (自 C++26 起)
合約斷言允許程式設計師指定程式在執行過程中,於特定點預期應成立的狀態屬性。
目錄 |
[編輯] 說明
合約斷言由 函式合約限定符 與 contract_assert 陳述式引入。每個合約斷言都有一個謂詞 (predicate),它是一個型別為 bool 的運算式。
[編輯] 評估合約斷言
合約斷言的評估會使用以下其中一種評估語義:
| 評估語義 | 是否為檢查語義 | 是否為終止語義 |
|---|---|---|
| ignore | ||
| observe (觀察) | 是 | |
| enforce (強制執行) | 是 | 是 |
| quick-enforce (快速強制執行) | 是 | 是 |
對於任何給定的合約斷言評估,使用哪種評估語義是由實作定義的。對於同一合約斷言的不同評估,評估語義可能有所不同,包含常數評估期間的評估。
若使用「ignore (忽略)」語義,合約斷言的評估不產生任何效果。
若使用檢查語義,合約斷言的評估 E 會確定謂詞的值。謂詞是否被評估是不明確的。如果滿足以下任一條件,則會發生合約違規:
- 評估該謂詞所得的結果值為 false。
- 謂詞的評估因例外而退出。
- 謂詞的評估是在被明確常數評估 (manifestly constant-evaluated)的環境中進行,且該謂詞並非核心常數運算式。
存在一個發生在 E 之前的可觀察檢查點 (observable checkpoint) CP,使得任何其他發生在 A 之前的作業 OP,也都發生在 CP 之前。
int num = 0; void f() pre((num++, false)); f(); // Increment of “num” might not occur, even if a checking semantic is used
[編輯] 處理合約違規
如果合約違規發生在明確常數評估的環境中:
- 若評估語義為「observe」,則會產生診斷訊息。
- 若評估語義為終止語義,則程式格式錯誤 (ill-formed)。
如果合約違規發生在非明確常數評估的環境中:
- 若評估語義為「quick-enforce」,程式將被合約終止。
- 若評估語義為「enforce」或「observe」,合約違規處理常式將被呼叫,並傳入一個參照至型別為 const std::contracts::contract_violation 之物件 obj 的左值,該物件包含有關合約違規的資訊。
- obj 的儲存空間以未指定的方式分配,但不會呼叫任何全域分配函式。
- obj 的生命週期持續到合約違規處理常式呼叫結束為止。
[編輯] 合約終止程式
當程式被合約終止時,(視環境而定)是否發生以下情況是由實作定義的:
- 呼叫 std::terminate,
- 呼叫 std::abort,或
- 執行終止(不再發生進一步的執行步驟)。
[編輯] 合約違規處理常式
程式的合約違規處理常式是一個名為 ::handle_contract_violation 的函式:
| void handle_contract_violation( std::contracts::contract_violation ); |
(C++26 起) (可選擇性加上 noexcept) |
|
合約違規處理常式的定義,稱為預設合約違規處理常式,由實作(而非標準函式庫標頭檔)提供。
合約違規處理常式是否可以替換是由實作定義的。如果合約違規處理常式不可替換,則宣告一個替換函式是不合法的,且不需要診斷。
當合約違規處理常式正常返回時:
- 若評估語義為「observe」,控制流程在合約斷言評估點之後正常繼續。
- 若評估語義為「enforce」,程式將被合約終止。
存在一個發生在合約違規處理常式正常返回之後的可觀察檢查點 CP,使得任何其他在處理常式返回之後發生的作業 OP,也都發生在 CP 之後。
[編輯] 處理來自斷言的例外
如果合約違規是因為謂詞評估因例外而退出,且評估語義為「observe」或「enforce」,則合約違規處理常式會從該例外的隱式處理常式 (handler) 內被呼叫。
當合約違規處理常式正常返回時:
- 若評估語義為「observe」,該隱式處理常式將不再被視為活躍。
- 若評估語義為「enforce」,當合約終止發生時,該隱式處理常式仍保持活躍。
當前的例外可以在合約違規處理常式內,使用 std::current_exception() 進行檢查或重新拋出。
[編輯] 按順序評估
若要按順序評估合約斷言列表 R:
S,使得滿足以下所有條件:R的所有元素都在S中。R中的每個元素在S中可重複出現實作定義的次數。- 如果合約斷言
A在R中位於合約斷言B之前,則A的第一次出現必須在S中位於B的第一次出現之前。
void f(int i) { contract_assert(i > 0); // #1 contract_assert(i < 10); // #2 // valid sequence of evaluations: #1 #2 (no repeat) // valid sequence of evaluations: #1 #1 #2 #2 (repeat in sequence) // valid sequence of evaluations: #1 #2 #1 #2 (repeat alternatively) // valid sequence of evaluations: #1 #2 #2 #1 (second occurences can switch order) // invalid sequence of evaluations: #2 #1 (first occurences cannot switch) }
[編輯] 附註
可用的評估語義選擇之範圍與靈活性取決於實作,且不必允許所有四種評估語義作為選項。
當合約斷言具有會改變常數運算式產生的值之副作用時,若在不同的轉譯單元中為相同的合約斷言選擇了不同的評估語義,可能會導致違規單一定義規則 (ODR)。
constexpr int f(int i) { contract_assert((++const_cast<int&>(i), true)); return i; } inline void g() { int a[f(1)]; // size dependent on the evaluation semantic of contract_assert above }
如果評估謂詞所得的結果值為 true,則不會發生合約違規,且控制流程在合約斷言評估點之後正常繼續。
如果謂詞的評估是通過非局部跳轉 (non-local jumps) 或終止程式而退出,也不會發生合約違規。
C++ 標準建議,預設合約違規處理常式應產生診斷輸出,適當地格式化參數中最相關的內容(針對重複出現的觀察合約違規進行頻率限制),然後正常返回。
| 特性測試巨集 | 數值 | 標準 | 功能 |
|---|---|---|---|
__cpp_contracts |
202502L |
(C++26) | 合約 |
[編輯] 關鍵字
[編輯] 參見
contract_assert 陳述式 (C++26) |
在執行過程中驗證內部條件 |
| 函式合約限定符 (C++26) | 指定前置條件 (pre) 與後置條件 (post) |