契约断言 (C++26 起)
契约断言允许程序员指定程序状态的属性,这些属性预计在执行期间的某些点保持有效。
目录 |
[编辑] 解释
契约断言由函数契约说明符和contract_assert
语句引入。每个契约断言都有一个谓词,它是一个bool类型的表达式。
[编辑] 评估契约断言
契约断言的评估使用以下评估语义之一
评估语义 | 是检查语义 | 是终止语义 |
---|---|---|
ignore | ||
observe | 是 | |
enforce | 是 | 是 |
quick-enforce | 是 | 是 |
对于契约断言的任何给定评估,使用哪种评估语义是实现定义的。对于同一契约断言的不同评估,包括常量评估期间的评估,评估语义可以不同。
如果使用“ignore”语义,则契约断言的评估没有效果。
如果使用检查语义,则契约断言的评估E
确定谓词的值。是否评估谓词是未指定的。如果满足以下任何条件,则发生契约违规
存在一个可观察的检查点 CP
,它发生在E
之前,使得在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”,则会产生诊断。
- 如果评估语义是终止语义,则程序格式错误。
如果契约违规发生在非显然常量评估的上下文中
- 如果评估语义是“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”,则契约违规处理器会在该异常的活动隐式处理程序中被调用。
当契约违规处理器正常返回时
- 如果评估语义是“observe”,则隐式处理程序不再被视为活动。
- 如果评估语义是“enforce”,则当契约终止发生时,隐式处理程序仍处于活动状态。
可以使用std::current_exception()在契约违规处理器中检查或重新抛出当前异常。
[编辑] 按顺序评估
要按顺序评估契约断言列表R
S
,使所有以下条件都得到满足R
的所有元素都在S
中。R
的每个元素可以在S
中重复实现定义次数。- 如果契约断言
A
在R
中先于另一个契约断言B
,则A
的第一次出现先于B
的第一次出现在S
中。
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++ 标准建议默认契约违规处理器应产生适当格式化参数最相关内容的诊断输出(对观察到的契约断言的潜在重复违规进行速率限制),然后正常返回。
功能测试宏 | 值 | 标准 | 特性 |
---|---|---|---|
__cpp_contracts |
202502L |
(C++26) | 契约 |
[编辑] 关键词
[编辑] 另请参阅
contract_assert 语句 (C++26) |
在执行期间验证内部条件 |
函数契约说明符 (C++26) | 指定前置条件 (pre) 和后置条件 (post) |