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

c++26新功能—契约编程

一、契约编程说明和定义

在C++编程中,安全始终是一个至关重要的问题。特别是随着近些年来,新语言的大量推出,特别是Rust语言,给C++标准委员会的大佬们创造了不少的压力。对广大的C++开发者来说,C++语言本身就是门不容易掌握的语言,里面的各种安全细节数不胜数。
不过,C++新标准的不断迭代,对这些安全问题都在不断的进行完善和修改。而本文提到的C++26中的契约就是一个旨在提高安全性和正确性的新特性。
Contracts,合约,契约。但从目前看,翻译成契约可能会更容易理解一些,毕竟区块链中,有一个合约的定义,很容易让一般开发者与其混淆。契约是一种编程范式的体现,它定义了一组软件安全规范,描述了预期的行为,用来增加代码的可读性、安全性和可维护性。它可以替代传统的assert的断言处理。在英文文档中描述为“Contract assertions allow the programmer to specify properties of the state of the program that are expected to hold at certain points during execution. ”

二、契约编程的内容

C++中的契约编程,分为前置条件(pre)、后置条件(post)和断言 (contract_assert)三种情况。契约断言由函数契约说明符和Contract_assert语句引入。每个契约断言都有一个谓词 ,它是bool类型的表达式。在代码应用的过程中,可以嵌入相关的条件限制,通过多级别的检查(参看下文),来增强代码的鲁棒性。在C++26中对编译器提供了-fcontract选项,用来支持四种语义(ignore/observe/quick-enforce/enforce)检查,用来解决调试与性能之间的冲突。四种语义的说明如下:
忽略(ignore):不检查即忽略断言
观察(observe):检查断言;若检查失败,打印消息并继续执行
快速强制执行(quick-enforce):检查断言;若检查失败,立即终止程序,不打印消息或执行任何其他操作
强制执行(enforce):检查断言;若检查失败,打印消息并终止程序(默认行为)

契约编程的形式主要有:
1、契约说明符
前置和后置说明符可以称为函数契约说明符,它可以在函数或Lambda表达式中应用
2、断言语句
断言语句是一种契约断言,可以在函数体或lambda表达式中作为语句出现。断言语句的关键字是contract_assert,后面是括号中的谓词,再后面是分号。这个关键字出现的目的是为了与原来的assert宏冲突。

看一个综合的例子:

int demo(const int a)pre (a != -1) // 前置说明符 ,pre,post为关键字post (r : r != 3) // 后置说明符:使用冒号与谓词分离,r为函数的结果对象引入的名称 
{contract_assert (a != 2); // 契约断言return a;
}

契约说明符出现在函数声明符的末尾,在返回类型和require子名后,在分号前。对于Lambda表达式,函数契约说明符出现在函数体之前。

int demo(const int a){auto func = [&](int d)pre(d > 0){...};return 0
}

需要说明的是,pre和post没有强制的顺序要求。
在C++26标准中,还提供了自定义契约违反处理程序的接口,通过这个接口可以将默认的消息打印,转化开发者需要的错误控制行为。这个接口名称为handle_contract_violation,其定义为:

void handle_contract_violation(const contract_violation&)

contract_violation参数允许访问违反断言的相关信息,如源码位置、评估语义以及异常的抛出等。

三、例程

看一下标准文档中提供的相关例程:

#include <array>
#include <cmath>
#include <concepts>
#include <contracts>
#include <limits>
#include <print>template <std::floating_point T>
constexpr auto is_normalizable(const std::array<T, 3>& vector) noexcept
{const auto& [x, y, z]{vector};const auto norm{std::hypot(x, y, z)};return std::isfinite(norm) && norm > T {0};
}template <std::floating_point T>
constexpr auto is_normalized(const std::array<T, 3>& vector) noexcept
{const auto& [x, y, z]{vector};const auto norm{std::hypot(x, y, z)};constexpr auto tolerance{010 * std::numeric_limits<T>::epsilon()};if (!is_normalizable(norm)) [[unlikely]]return false;return std::abs(norm - T{1}) <= tolerance;
}//下面的代码提供了两种条件限制
template <std::floating_point T>
constexpr auto normalize(std::array<T, 3> vector) noexcept -> std::array<T, 3>pre(is_normalizable(vector))post(vector: is_normalized(vector))
{auto& [x, y, z]{vector};const auto norm{std::hypot(x, y, z)};x /= norm, y /= norm, z /= norm;return vector;
}int main()
{const auto v = normalize<float>({0.3, 0.4, 0.5});std::println("{}", v);const auto w = normalize<float>({0, 0, 0}); // violates pre- and post- conditionsstd::println("{}", w);
}

注意:代码需要在支持C++26的环境运行。

四、总结

通过契约(合约)在C++26中的提出可以看到,C++的安全性已经被提高了一个层次,不再简单的归结于原来的小打小闹的修补,已经开始成模块,成单元的来解决。这是一种非常好的发展的趋势和方向。不要被一些思潮所诱导,C++语言会如何发展,不是靠言语可以左右的。
不妨让子弹多飞一会儿,要有耐心,耐心是一种美德。

http://www.xdnf.cn/news/14223.html

相关文章:

  • 单测时如何让 mock 的接口在长链路调用时一直生效
  • 从STM32到NXP:GPIO就像装修房子,多了个“智能开关”
  • 基于 SpringBoot+Servlet+JSP 的医院医保管理系统的设计与实现,论文7000字,可根据实际情况调整
  • ES+索引库文档操作
  • [CVPR 2025] DiCo:动态协作网络助力半监督3D血管分割新突破
  • AI Agent实战 - LangChain+Playwright构建火车票查询Agent
  • 人工智能学习28-BP过拟合
  • [k8s]--exec探针详细解析
  • java常见第三方依赖以及相关安全问题
  • http1.x VS http2.x 协议
  • Spring Cloud Alibaba 中间件
  • 硬编码(修改RIP相关指令)
  • HTML+CSS 半透明登录框
  • (LeetCode每日一题) 2566. 替换一个数字后的最大差值 ( 贪心 )
  • 安防市场的中小企业突围——从竞品分析到破局路径的思考
  • Spring Boot中Controller层规划与最佳实践详解
  • 【北京迅为】iTOP-4412精英版使用手册-第二十一章 延时函数专题
  • Python爬虫-批量爬取快手视频并将视频下载保存到本地
  • BeckHoff PLC --> 料筐(KLT Box)自动对中与抓取程序分析
  • Deep Research Agent的深度与广度如何保证
  • OSGI 是什么,有哪些具体应用、java8、9、10、11比较
  • C++操作系统与网络编程(针对特定岗位)
  • SpringBoot打包运行原理和加载机制原理
  • 从大数据到大模型:我们是否在重蹈覆覆辙
  • 一文详解前缀和:从一维到二维的高效算法应用
  • Java相关-链表-设计链表-力扣707
  • JS进阶 Day02
  • 在tensorrt engine中提高推理性能小记
  • 互联网大厂Java求职面试:云原生架构与微服务设计中的复杂挑战
  • Flask文件上传与异常处理完全指南