当前位置: 首页 > news >正文

C++26 函数契约(Contract)概览

文章目录

    • 1. 什么是契约编程?
      • 契约编程的三大核心:
    • 2. C++26 契约编程的语法
      • 语法示例
    • 3. 契约检查模式
      • 3.1. `default` 模式
      • 3.2. `audit` 模式
      • 3.3. `axiom` 模式
      • 检查模式的设置
    • 4. 契约编程与传统 `assert` 的区别
      • 示例对比
    • 5. 契约编程的应用场景
    • 6. 注意事项
    • 7. 示例: 带契约的矩形面积计算
    • 8. 总结

契约编程(Contracts)预计成为 C++26 语言标准的一部分. 这一特性为开发者提供了一种标准化的方式来表达函数的前置条件, 后置条件以及断言, 从而显著提升代码的可读性, 可靠性和可维护性. 在本文中, 我们将深入探讨契约编程的概念, 语法, 检查模式以及实际应用场景, 帮助您快速掌握这一强大的新特性.


1. 什么是契约编程?

契约编程起源于软件工程的一种理念, 最早由 Bertrand Meyer 在 Eiffel 语言中提出. 它的核心思想是将函数的 输入约束(前置条件)输出约束(后置条件) 明确化, 形成一种程序逻辑上的契约. 这种契约不仅有助于描述函数的行为, 还能用于运行时验证, 帮助开发者快速发现问题.

契约编程的三大核心:

  1. 前置条件(Preconditions):
    函数执行前必须满足的条件, 由调用者负责. 例如, 输入参数是否有效.

  2. 后置条件(Postconditions):
    函数执行完成后必须满足的条件, 由实现者负责. 例如, 返回值是否符合预期.

  3. 断言(Assertions):
    程序执行过程中必须满足的逻辑条件, 用于验证程序内部状态是否正确.


2. C++26 契约编程的语法

C++26 提供了三个新的属性标记, 专门用于定义契约:

  • [[pre: ...]]: 定义前置条件.
  • [[post: ...]]: 定义后置条件.
  • [[assert: ...]]: 定义断言.

语法示例

以下是一个简单的除法函数, 展示了契约编程的基本用法:

#include <iostream>int divide(int a, int b)[[pre:b != 0]]                   // 前置条件: 除数不能为零[[post result:result == a / b]]  // 后置条件: 返回值必须等于 a / b
{return a / b;
}int main() {divide(10, 0);  // 触发前置条件失败, 运行时终止return 0;
}

输出:

Program returned: 139
contract violation in function divide at /app/example.cpp:3: b != 0
terminate called without an active exception
Program terminated with signal: SIGSEGV

在 Compiler Explorer 中查看


3. 契约检查模式

契约编程不仅仅是简单的运行时检查, 它通过 检查模式 提供了灵活的验证方式. C++26 提供了三种检查模式:

3.1. default 模式

  • 默认模式, 用于开发阶段.
  • 对所有契约条件进行验证, 如果检查失败, 程序终止并报告错误.
  • 适合日常开发和调试.

3.2. audit 模式

  • 加强模式, 用于深度检查.
  • 包括性能敏感路径中的契约条件.
  • 性能开销较高, 适合可靠性要求高的复杂场景.

3.3. axiom 模式

  • 假设模式, 用于生产环境.
  • 假定所有契约条件始终成立, 不进行运行时检查.
  • 编译器可以利用这些契约条件优化代码, 例如移除不必要的分支.

检查模式的设置

可以通过编译器选项控制契约检查模式, 例如:

  • GCC 示例
    g++ -std=c++26 -fcontracts -fcontract-build-level=default main.cpp   # 使用 default 模式
    g++ -std=c++26 -fcontracts -fcontract-build-level=audit main.cpp    # 使用 audit 模式
    g++ -std=c++26 -fcontracts -fcontract-build-level=off main.cpp    # 使用 axiom 模式
    

4. 契约编程与传统 assert 的区别

C++26 的契约与传统的 assert 语句在设计目标和功能上有显著差异:

特性契约(Contracts)assert
目标定义函数的行为约定, 明确调用和实现责任.临时检查某些条件是否满足, 主要用于调试.
语法专用关键字: [[pre:]], [[post:]].标准库函数: assert(condition).
范围全局行为约定, 包括前置条件, 后置条件和断言.局部检查, 仅在代码块中验证条件.
运行时模式支持 default, auditaxiom 模式.只有简单的 NDEBUG 开关控制检查启用.

示例对比

契约:

int divide(int a, int b)[[pre: b != 0]]               // 契约描述行为约定
{return a / b;
}

assert:

int divide(int a, int b) {assert(b != 0);               // 仅用于调试阶段的检查return a / b;
}

5. 契约编程的应用场景

  1. 高可靠性系统:

    • 航空航天, 自动驾驶, 金融系统等需要严格保证代码正确性.
    • 契约能够防止运行时出现未定义行为.
  2. 公共 API 开发:

    • 通过契约明确调用者的责任和函数行为, 为用户提供清晰的文档.
  3. 复杂算法实现:

    • 使用契约验证中间结果, 确保算法逻辑正确.
  4. 生产环境优化:

    • 在生产模式下切换到 axiom 模式, 移除所有运行时检查, 提升性能.

6. 注意事项

  1. 运行时开销:

    • defaultaudit 模式会增加运行时检查开销, 在性能敏感场景需谨慎使用.
  2. 编译器支持:

    • C++26 的契约编程尚未被所有主流编译器完全支持, 需要注意工具链的更新情况.
  3. 与异常处理的区别:

    • 契约不用于替代异常处理, 它更注重逻辑验证而非用户友好性.

7. 示例: 带契约的矩形面积计算

以下代码展示如何为类方法添加契约:

#include <iostream>struct Rectangle {int width;int height;int area() const[[pre:width > 0 && height > 0]]  // 前置条件: 长和宽必须为正[[post result:result > 0]]       // 后置条件: 面积必须为正{return width * height;}
};int main() {Rectangle rect{5, 10};std::cout << "Area: " << rect.area() << std::endl;Rectangle invalid{-5, 10};std::cout << invalid.area(); // 触发前置条件失败return 0;
}

在 Compiler Explorer 中查看代码

注意在三种不同模式下的输出不尽相同:

  1. default/audit:
    contract violation in function Rectangle::area at /app/example.cpp:7: width > 0 && height > 0
    terminate called without an active exception
    Program terminated with signal: SIGSEGV
    Area: 50
    
  2. axiom:
    Area: 50
    -50
    

8. 总结

契约编程通过前置条件, 后置条件和断言, 为开发者提供了一个全面的工具来提升代码质量. 在开发阶段, 契约编程有助于快速发现问题; 在生产环境中, 通过合理选择检查模式, 可以兼顾性能与安全性.

契约编程不仅是一种语言特性, 更是现代软件开发中提升代码可靠性的重要工具. 随着编译器支持的逐步完善, 它将成为每位 C++ 开发者的得力助手!

相关文章:

C++26 函数契约(Contract)概览

文章目录 1. 什么是契约编程?契约编程的三大核心: 2. C26 契约编程的语法语法示例 3. 契约检查模式3.1. default 模式3.2. audit 模式3.3. axiom 模式检查模式的设置 4. 契约编程与传统 assert 的区别示例对比 5. 契约编程的应用场景6. 注意事项7. 示例: 带契约的矩形面积计算…...

Flink CDC 自定义函数处理 SQLServer XML类型数据 映射 doris json字段方案

Flink CDC 自定义函数处理 SQLServer XML类型数据方案 1. 背景 因业务使用SQLServer数据库&#xff0c;CDC同步到doris 数仓。对于SQLServer xml类型&#xff0c;doris没有相应的字段对应&#xff0c; 可以使用json来存储xml数据。需要进行一步转换。从 flink 自定义函数入手…...

F.interpolate函数

F.interpolate 是 PyTorch 中用于对张量&#xff08;通常是图像数据&#xff09;进行插值操作的函数&#xff0c;常用于调整张量的大小&#xff0c;例如改变图像的分辨率。它支持多种插值方法&#xff0c;包括最近邻插值、双线性插值和三次插值等。 语法 torch.nn.functional…...

华为交换机---自动备份配置到指定ftp/sftp服务器

华为交换机—自动备份配置到指定ftp服务器 需求 交换机配置修改后及时备份相关配置,每次配置变化后需要在1分钟后自动进行保存,并且将配置上传至FTP服务器;每隔30分钟,交换机自动把配置上传到FTP服务器。 1、定时保存新配置的时间间隔为*分钟(1天=1440),默认为30分钟(…...

nginx学习之路-nginx配置https服务器

文章目录 1. 生成证书2. 配置证书1. 拷贝证书文件2. 修改conf/nginx.conf文件内容 3. 查看效果1. 重载配置2. 访问 1. 生成证书 在linux系统下执行&#xff0c;使用openssl命令。&#xff08;windows环境也可以使用cmder&#xff09; # 1. 生成私钥 server2025.key(无密码保护…...

UCAS 24秋网络认证技术 CH10 SSL 复习

TLS字段、参数含义要了解每个消息是什么意思 基本方式只验证服务端&#xff0c;服务端有证书&#xff0c;变形方式加上验证客户端TLS1.3区别 协商过程 背景 Record层使用的各种加密算法参数&#xff0c;均由Handshake协议协商获得。 具体过程 随机数交换 Client/Server相互…...

【linux内核分析-存储】EXT4源码分析之“文件删除”原理【七万字超长合并版】(源码+关键细节分析)

EXT4源码分析之“文件删除”原理【七万字超长合并版】&#xff08;源码关键细节分析&#xff09;&#xff0c;详细的跟踪了ext4文件删除的核心调用链&#xff0c;分析关键函数的细节&#xff0c;解答了开篇中提出的三个核心疑问。 文章目录 提示前言全文重点索引1.源码解析1.1 …...

代码随想录 day62 第十一章 图论part11

第十一章&#xff1a;图论part11 Floyd 算法精讲 Floyd 算法代码很简单&#xff0c;但真正理解起原理 还是需要花点功夫&#xff0c;大家在看代码的时候&#xff0c;会发现 Floyd 的代码很简单&#xff0c;甚至看一眼就背下来了&#xff0c;但我为了讲清楚原理&#xff0c;本…...

springboot571基于协同过滤算法的私人诊所管理系统(论文+源码)_kaic

摘 要 随着时代的发展&#xff0c;人们的生活方式得到巨大的改变&#xff0c;从而慢慢地出现了大量私人诊所信息&#xff0c;私人诊所信息管理需要一个现代化的管理系统&#xff0c;进行私人诊所的管理。 私人诊所管理系统的开发就是为了解决私人诊所信息管理的问题&#xff0…...

Uniapp Android 本地离线打包(详细流程)

一、简介 App 离线 SDK 暂时不支持 Kotlin&#xff0c;未来不清楚。 uniapp 提供了 云打包 与 本地打包 两种方案&#xff0c;云打包 需要排队且还有次数限制&#xff0c;本地打包 则就没有这些限制&#xff0c;而且会 本地打包 对开发 原生插件 有很大的帮助。 细节&#x…...

vite+vue3动态引入资源文件(问题已解决但离了个大谱)

教程很详细&#xff0c;直接上代码 解决方法&#xff08;赶时间的小友理解下这函数就能解决问题了&#xff0c;就是处理了下路径&#xff0c;运气不好遇到问题再回来也不迟&#x1f923;&#x1f923;&#x1f923;&#xff09; const getSvgUrl (name) > {// name: svg_1…...

通过 4 种方式快速将音乐从 iPod 传输到 Android

概括 在 iPod 上听音乐很酷&#xff0c;但是当您拥有最新的 Android 手机时&#xff0c;也许您想在新手机上欣赏 iPod 音乐。那么&#xff0c;你的计划是什么&#xff1f;如何将音乐从 iPod 传输到 Android&#xff1f; 如果您担心这个问题&#xff0c;请看看下面的方法。他们…...

ArcGIS中怎么把数据提取到指定范围(裁剪、掩膜提取)

最近&#xff0c;经常能收到怎么把数据提取到指定范围、栅格数据怎么裁剪、矢量数据怎么裁剪、栅格数据怎么掩膜提取的咨询。 下面是我对这个问题的解决思路&#xff1a; 对于矢量数据&#xff1a; ①首先把数据加载进来 ②软件界面上面的工具栏找到→地理处理→裁剪&#x…...

【Vaadin flow 实战】第3讲-快速上手构建VaadinFlow+Springboot的全栈web项目

快速构建VaadinFlowSpringboot的全栈web项目 温馨提示&#xff0c;本文讲解比较精炼&#xff0c;主要以快速上手开发为主。 官方提供了与本文类似的教程讲解&#xff0c;地址https://vaadin.com/docs/latest/getting-started 1访问vaadin官方提供的start网站(类似于 spring i…...

HBase Cassandra的部署和操作

目录 一&#xff0e;数据库的部署与配置 二&#xff0e;使用命令访问数据库 三&#xff0e;数据库的设计 四&#xff0e;编程实现数据库的访问 一&#xff0e;数据库的部署与配置 1.在单个节点上对进行数据库的单机部署 &#xff08;1&#xff09;下载apache-cassandra-4.1.7-…...

用户界面软件01

Jens Coldewey 著&#xff0c;Tom.X 译 本文中的模式语言逐步深入地探讨用户界面架构的设计&#xff0c;它基于人机工程学&#xff0c;足以形成一套完整的体系。如果你对这方面有兴趣&#xff0c;请参考[Tog92]&#xff0c;[Coo95]和[Col95]。 本文不讨论用户界面的布局&…...

【云原生】Docker Compose 从入门到实战使用详解

目录 一、前言 二、Docker Compose 介绍 2.1 Docker Compose概述 2.2 Docker Compose特点 2.3 Docker Compose使用场景 三、Docker Compose 安装 3.1 安装docker环境 3.2 Docker Compose安装方式一 3.2.1 下载最新版 3.2.2 设置权限 3.2.3 设置软链接 3.2.4 查看版本…...

【ShuQiHere】使用 SCP 进行安全文件传输

【ShuQiHere】&#x1f680; 在日常的开发和运维工作中&#xff0c;文件传输是一个常见的任务。scp&#xff08;Secure Copy&#xff09;是一个基于 SSH 协议的文件传输工具&#xff0c;能够在本地和远程主机之间安全地复制文件和目录。本文将详细介绍 scp 的使用方法&#xf…...

海康威视H5player问题汇总大全

由于除了要支持Windows平台&#xff0c;还要支持国产系统的平台&#xff0c;这时就用到了H5player&#xff0c;但是这个在使用调试的时候会遇到各种各样的问题&#xff0c;便在此分享一下&#xff0c;供大家分享&#xff01;&#xff01;&#xff01; 问题一&#xff1a;Unexp…...

力扣23.合并K个升序链表

文章目录 一、前言二、最小堆解法三、分治解法 一、前言 23. 合并 K 个升序链表 本题的要求是把K个链表进行合并&#xff0c;合并后的链表必须是从小到大的。 并且这K个链表也是从小到大的升序链表。 二、最小堆解法 既然每个链表都是升序的&#xff0c;也就是从小到大的。 …...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

对象回调初步研究

_OBJECT_TYPE结构分析 在介绍什么是对象回调前&#xff0c;首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例&#xff0c;用_OBJECT_TYPE这个结构来解析它&#xff0c;0x80处就是今天要介绍的回调链表&#xff0c;但是先不着急&#xff0c;先把目光…...

如何做好一份技术文档?从规划到实践的完整指南

如何做好一份技术文档&#xff1f;从规划到实践的完整指南 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...