Contract assertions (since C++26)
契約表明(Contract assertions)により、プログラマはプログラム実行中の特定の点で保持されることが期待されるプログラム状態の特性を指定できます。
目次 |
説明
契約表明
は
関数契約指定子
および
contract_assert
文によって導入されます。各契約表明は
述語
を持ち、これは
bool
型の式です。
契約アサーションの評価
コントラクト表明の評価は、以下の評価セマンティクスのいずれかを使用します:
| 評価セマンティクス | チェック機能を持つか | 終了機能を持つか |
|---|---|---|
| ignore | ||
| observe | はい | |
| enforce | はい | はい |
| quick-enforce | はい | はい |
どの評価セマンティクスが契約表明の任意の評価に対して使用されるかは実装定義です。評価セマンティクスは、同じ契約表明の異なる評価間で異なる可能性があり、定数評価中の評価も含まれます。
「ignore」セマンティクスが使用された場合、契約アサーションの評価は効果を持ちません。
チェックセマンティクスが使用される場合、契約アサーションの評価
E
が述語の値を決定します。述語が評価されるかどうかは未規定です。以下のいずれかの条件が満たされる場合、
契約違反
が発生します:
- 述語を評価した結果の値が false となる場合。
- 述語の評価が例外によって終了する場合。
- 述語の評価が 明示的に定数評価される コンテキストで実行され、かつ述語が コア定数式 ではない場合。
観測可能なチェックポイント
CP
が
E
の前に発生し、
A
の前に発生する他のあらゆる操作
OP
も
CP
の前に発生する。
int num = 0; void f() pre((num++, false)); f(); // チェックセマンティクスが使用された場合でも、「num」のインクリメントが発生しない可能性がある
契約違反の処理
契約違反が明らかに定数評価される文脈で発生した場合:
- 評価セマンティクスが「observe」の場合、診断が生成されます。
- 評価セマンティクスが終了セマンティクスである場合、プログラムは不適格となります。
契約違反が明示的に定数評価されていない文脈で発生した場合:
- 評価セマンティクスが「quick-enforce」の場合、プログラムは契約終了されます。
-
評価セマンティクスが「enforce」または「observe」の場合、契約違反ハンドラが呼び出され、型
const
std
::
contracts
::
contract_violation
のオブジェクト
obj
を参照する左辺値が渡されます。このオブジェクトには契約違反に関する情報が含まれています。
- obj のストレージは未指定の方法で割り当てられますが、グローバルな アロケーションファンクション は呼び出されません。
- obj の寿命は、契約違反ハンドラの呼び出し期間中持続します。
契約終了プログラム
プログラムが contract-terminated された場合、実装定義(文脈に依存)で以下のいずれかが発生します
- std::terminate が呼び出される、
- std::abort が呼び出される、または
- 実行が終了する(それ以上の 実行ステップ が発生しない)。
契約違反ハンドラ
プログラムの contract-violation handler は、 :: handle_contract_violation という名前の関数です:
|
void
handle_contract_violation
(
std
::
contracts
::
contract_violation
)
;
|
(C++26以降)
(オプションで noexcept) |
|
契約違反ハンドラーの定義は、実装によって(標準ライブラリヘッダーの代わりに)提供され、 デフォルト契約違反ハンドラー と呼ばれます。
契約違反ハンドラが replaceable であるかどうかは実装定義です。契約違反ハンドラがreplaceableでない場合、契約違反ハンドラのreplacement functionの宣言は診断不要のill-formedとなります。
契約違反ハンドラが正常に戻った場合:
- 評価セマンティクスが「observe」の場合、コントラクト表明の評価ポイントの後で制御フローは通常通り継続します。
- 評価セマンティクスが「enforce」の場合、プログラムはコントラクト終了となります。
契約違反ハンドラが正常に戻った後に発生する
観測可能なチェックポイント
CP
が存在し、これにより契約違反ハンドラが戻った後に発生する他のあらゆる操作
OP
も
CP
の後に発生することが保証される。
アサーションからの例外の処理
契約違反が述語の評価が例外によって終了したために発生し、評価セマンティクスが「observe」または「enforce」である場合、契約違反ハンドラはその例外に対するアクティブな暗黙の handler 内から呼び出されます。
契約違反ハンドラが正常に戻った場合:
- 評価セマンティクスが「observe」の場合、暗黙のハンドラはもはやアクティブとは見なされません。
- 評価セマンティクスが「enforce」の場合、暗黙のハンドラは契約終了発生時にもアクティブな状態を維持します。
現在の例外は、契約違反ハンドラ内で std::current_exception() を使用して検査または再スローすることができます。
順次評価
契約アサーションのリスト
R
を
順次評価
するには:
S
を構築せよ:
-
Rのすべての要素がSに含まれる。 -
Rの各要素は実装定義の回数だけS内で繰り返し可能である。 -
契約表明
Aが契約表明BよりR内で先行する場合、S内におけるAの最初の出現はBの最初の出現より先行する。
void f(int i) { contract_assert(i > 0); // #1 contract_assert(i < 10); // #2 // 有効な評価シーケンス: #1 #2 (繰り返しなし) // 有効な評価シーケンス: #1 #1 #2 #2 (順序通りに繰り返し) // 有効な評価シーケンス: #1 #2 #1 #2 (交互に繰り返し) // 有効な評価シーケンス: #1 #2 #2 #1 (2回目の出現は順序を入れ替え可能) // 無効な評価シーケンス: #2 #1 (最初の出現は順序を入れ替え不可) }
注記
利用可能な評価セマンティクスの選択肢の範囲と柔軟性は実装に依存し、4つの評価セマンティクスすべてを可能性として許可する必要はありません。
同じ契約表明に対して異なる翻訳単位で選択された異なる評価セマンティクスは、契約表明が定数式によって生成される値を変更する副作用を持つ場合、 one-definition rule の違反を引き起こす可能性があります:
constexpr int f(int i) { contract_assert((++const_cast<int&>(i), true)); return i; } inline void g() { int a[f(1)]; // サイズは上記のcontract_assertの評価セマンティクスに依存 }
述語を評価した結果の値が true である場合、契約違反は発生せず、制御フローは契約表明の評価点の後で通常通り継続します。
述語の評価が non-local jumps またはプログラムの終了によって終了する場合も、契約違反は発生しません。
C++標準では、デフォルトの契約違反ハンドラは、引数の最も関連性の高い内容を適切にフォーマットした診断出力を生成し(観測された契約アサーションの潜在的に繰り返される違反に対してレート制限を適用)、その後正常に戻ることを推奨しています。
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_contracts
|
202502L
|
(C++26) | Contracts |
キーワード
contract_assert , pre , post
サポート状況
|
C++26 機能
|
提案文書
|
GCC
|
Clang
|
MSVC
|
Apple Clang
|
EDG eccp
|
Intel C++
|
Nvidia HPC C++ (ex PGI)*
|
Nvidia nvcc
|
Cray
|
|---|---|---|---|---|---|---|---|---|---|---|
| Contracts ( FTM ) * | P2900R14 |
関連項目
contract_assert
文
(C++26)
|
実行中の内部条件を検証する |
| 関数契約指定子 (C++26) | 事前条件( pre )と事後条件( post )を指定する |