合约断言 (自 C++26 起)
合约断言允许程序员指定程序状态的属性,这些属性预计在执行期间的特定点保持为真。
内容 |
[编辑] 解释
合约断言 由 函数合约说明符 和 contract_assert
语句引入。每个合约断言都有一个谓词,它是一个 bool 类型的表达式。
[编辑] 评估合约断言
合约断言的评估使用以下评估语义之一
评估语义 | 是检查语义吗 | 是终止语义吗 |
---|---|---|
忽略 | ||
观察 | 是 | |
强制 | 是 | 是 |
快速强制 | 是 | 是 |
对于合约断言的任何给定评估,使用哪种评估语义是实现定义的。评估语义可以对于同一合约断言的不同评估而不同,包括常量求值期间的评估。
如果使用 “忽略” 语义,则合约断言的评估没有效果。
如果使用检查语义,则合约断言的评估 E
确定谓词的值。是否评估谓词是未指明的。如果满足以下任何条件,则会发生合约违规
存在一个在 E
之前发生的 可观察检查点 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
[编辑] 处理合约违规
如果在显式常量求值的上下文中发生合约违规
- 如果评估语义为 “观察”,则会生成诊断信息。
- 如果评估语义为终止语义,则程序是非良构的。
如果在非显式常量求值的上下文中发生合约违规
- 如果评估语义为 “快速强制”,则程序是合约终止的。
- 如果评估语义为 “强制” 或 “观察”,则会使用指向类型为 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) |
|
合约违规处理程序的定义,称为默认合约违规处理程序,由实现提供(而不是标准库头文件)。
合约违规处理程序是否是 可替换的 是实现定义的。如果合约违规处理程序不可替换,则替换合约违规处理程序的函数的声明是非良构的,不需要诊断。
当合约违规处理程序正常返回时
- 如果评估语义为 “观察”,则控制流在合约断言的评估点之后正常继续。
- 如果评估语义为 “强制”,则程序是合约终止的。
存在一个在合约违规处理程序正常返回后发生的 可观察检查点 CP
,使得在合约违规处理程序返回后发生的任何其他操作 OP
也发生在 CP
之后。
[编辑] 处理来自断言的异常
如果合约违规是由于谓词的评估通过异常退出而发生的,并且评估语义为 “观察” 或 “强制”,则从该异常的活动隐式 处理程序 内调用合约违规处理程序。
当合约违规处理程序正常返回时
- 如果评估语义为 “观察”,则不再认为隐式处理程序处于活动状态。
- 如果评估语义为 “强制”,则在发生合约终止时,隐式处理程序保持活动状态。
可以使用 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) }
[编辑] 注解
可用评估语义的范围和灵活性取决于实现,并且不需要允许所有四种评估语义作为可能性。
为不同翻译单元中的同一合约断言选择的不同评估语义可能导致违反 单一定义规则,当合约断言具有更改常量表达式产生值的副作用时
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,则不会发生合约违规,并且控制流在合约断言的评估点之后正常继续。
如果谓词的评估通过 非局部跳转 或终止程序退出,也不会发生合约违规。
C++ 标准建议默认合约违规处理程序应生成诊断输出,该输出适当格式化参数的最相关内容(对于可能重复发生的观察到的合约断言违规进行速率限制),然后正常返回。
特性测试宏 | 值 | Std | 特性 |
---|---|---|---|
__cpp_contracts |
202502L |
(C++26) | 合约 |
[编辑] 关键字
[编辑] 参见
contract_assert 语句 (C++26) |
验证执行期间的内部条件 |
函数合约说明符 (C++26) | 指定先决条件 (pre) 和后置条件 (post) |