C++20新特性进阶:概念实战——让代码更加类型安全和灵活

devtools/2024/9/20 2:06:23/ 标签: c++20, c++, C++20新特征

C++20新特性进阶:概念实战——让代码更加类型安全和灵活

C++20引入了一项革命性的特性——概念(Concepts),它允许你在编写模板代码时更加精确地描述类型的要求。概念不仅可以提升代码的可读性和可维护性,还能在编译时捕捉更多的错误。下面通过几个实战示例带你了解如何在C++20中使用概念。

1. 概念的基本语法

概念定义的基本语法如下:

concept <概念名称> = requires(<类型或值>) {// 约束条件
};

这里的<约束条件>可以是任何合法的C++表达式,只要该表达式在requires块中是有效的并且不会导致编译失败。

2. 概念定义的requires子句详解
基本形式
concept <概念名称> = requires(<类型或值>) {<表达式>;
};

这里的<表达式>可以是任何合法的C++表达式,只要该表达式在requires块中是有效的并且不会导致编译失败。

可以定义的概念约束

概念约束可以包含多种条件,以下是几种常见的约束条件:

  • 函数调用

    concept HasAddition = requires(T t) {t + t;  // 检查类型T是否支持加法运算
    };concept HasSubtraction = requires(T t) {t - t;  // 检查类型T是否支持减法运算
    };
    
  • 成员访问

    concept HasMemberFunction = requires(T t) {t.someMember();  // 检查类型T是否有成员函数someMember
    };concept HasMemberVariable = requires(T t) {t.someVariable;  // 检查类型T是否有成员变量someVariable
    };
    
  • 类型转换

    concept CanConvertToInt = requires(T t) {static_cast<int>(t);  // 检查类型T是否可以转换为int
    };
    
  • 构造函数

    concept CanBeConstructedFromInt = requires(T t) {T{1};  // 检查类型T是否可以从int构造
    };
    
  • 运算符重载

    concept SupportsMultiplication = requires(T t) {t * t;  // 检查类型T是否支持乘法运算
    };
    
  • 比较运算

    concept SupportsEquality = requires(T t) {t == t;  // 检查类型T是否支持等于运算
    };
    
3. 定义基础概念

首先,我们需要定义一些基础概念,这些概念描述了类型应该支持的基本操作。

// 定义支持加法运算的概念
concept Numeric = requires(T t) {t + t;  // 检查类型T是否支持加法运算
};// 定义支持减法运算的概念
concept HasSubtraction = requires(T t) {t - t;  // 检查类型T是否支持减法运算
};// 定义支持乘法运算的概念
concept SupportsMultiplication = requires(T t) {t * t;  // 检查类型T是否支持乘法运算
};// 定义支持除法运算的概念
concept SupportsDivision = requires(T t) {t / t;  // 检查类型T是否支持除法运算
};// 定义支持比较运算的概念
concept SupportsEquality = requires(T t) {t == t;  // 检查类型T是否支持等于运算
};// 定义支持构造函数的概念
concept CanBeConstructedFromInt = requires(T t) {T{1};  // 检查类型T是否可以从int构造
};
4. 定义复合概念

接着,我们可以定义一些复合概念,这些概念结合了多个基础概念,描述了类型应该支持的一组操作。

// 定义一个复合概念,表示支持基本的算术运算
concept ArithmeticType = Numeric && HasSubtraction && SupportsMultiplication && SupportsDivision;// 定义一个复合概念,表示支持基本的算术运算和比较运算
concept ArithmeticAndComparable = ArithmeticType && SupportsEquality;
5. 使用概念约束模板

有了概念之后,我们可以在模板定义中使用这些概念来限制模板参数的类型。

// 定义一个模板函数,仅当类型满足ArithmeticAndComparable概念时才有效
template<ArithmeticAndComparable T>
T compute(T a, T b) {return a + b * 2 - a / 2;
}
6. 定义范围概念

概念也可以用来描述容器类型,例如,定义一个范围(Range)概念,确保类型支持beginend方法。

// Range 视图示例
template<typename R>
concept IsRange = requires(R r) {{ std::ranges::begin(r) } -> std::same_as<decltype(std::ranges::end(r))>;{ std::ranges::end(r) } -> std::same_as<decltype(std::ranges::begin(r))>;
};template<IsRange R>
void processRange(R&& range) {for (auto&& elem : range) {std::cout << elem << " ";}std::cout << std::endl;
}
7. 实战应用

最后,我们来看一个实战应用范例的完整代码,包括定义概念、使用概念约束模板以及处理Range的示例。这段代码展示了如何利用C++20的概念特性来编写类型安全且灵活的代码。

#include <iostream>
#include <vector>
#include <ranges>// 定义支持加法运算的概念
concept Numeric = requires(T t) {t + t;  // 检查类型T是否支持加法运算
};// 定义支持减法运算的概念
concept HasSubtraction = requires(T t) {t - t;  // 检查类型T是否支持减法运算
};// 定义支持乘法运算的概念
concept SupportsMultiplication = requires(T t) {t * t;  // 检查类型T是否支持乘法运算
};// 定义支持除法运算的概念
concept SupportsDivision = requires(T t) {t / t;  // 检查类型T是否支持除法运算
};// 定义支持比较运算的概念
concept SupportsEquality = requires(T t) {t == t;  // 检查类型T是否支持等于运算
};// 定义支持构造函数的概念
concept CanBeConstructedFromInt = requires(T t) {T{1};  // 检查类型T是否可以从int构造
};// 定义一个复合概念,表示支持基本的算术运算
concept ArithmeticType = Numeric && HasSubtraction && SupportsMultiplication && SupportsDivision;// 定义一个复合概念,表示支持基本的算术运算和比较运算
concept ArithmeticAndComparable = ArithmeticType && SupportsEquality;// 定义一个模板函数,仅当类型满足ArithmeticAndComparable概念时才有效
template<ArithmeticAndComparable T>
T compute(T a, T b) {return a + b * 2 - a / 2;
}// Range 视图示例
template<typename R>
concept IsRange = requires(R r) {{ std::ranges::begin(r) } -> std::same_as<decltype(std::ranges::end(r))>;{ std::ranges::end(r) } -> std::same_as<decltype(std::ranges::begin(r))>;
};template<IsRange R>
void processRange(R&& range) {for (auto&& elem : range) {std::cout << elem << " ";}std::cout << std::endl;
}int main() {// 使用复合概念的compute函数auto result = compute(1, 2);  // 正确调用,因为int满足ArithmeticAndComparable概念// auto result2 = compute("Hello", "World");  // 编译错误,因为std::string不能满足ArithmeticAndComparable概念// 使用Range处理std::vector<int> nums = {1, 2, 3, 4, 5};processRange(nums);return 0;
}

代码解释

  1. 概念定义

    • NumericHasSubtractionSupportsMultiplicationSupportsDivisionSupportsEqualityCanBeConstructedFromInt 分别定义了支持加法、减法、乘法、除法、比较运算和从整数构造的概念。
    • ArithmeticTypeArithmeticAndComparable 是复合概念,分别表示支持基本的算术运算和支持基本的算术运算和比较运算。
  2. 模板函数compute

    • template<ArithmeticAndComparable T> 确保模板参数 T 必须满足 ArithmeticAndComparable 概念。
    • 函数体实现了简单的算术运算。
  3. 范围概念IsRange

    • template<typename R> 确保模板参数 R 必须满足 IsRange 概念。
    • 概念检查类型 R 是否支持 beginend 方法,并且返回的类型相同。
  4. 处理范围的函数processRange

    • 接收一个右值引用 R&& range,并使用范围for循环遍历 range
  5. 主函数main

    • 调用 compute 函数处理两个整数,因为 int 满足 ArithmeticAndComparable 概念。
    • 调用 processRange 函数处理一个整数向量 nums

通过上述代码,我们可以看到概念是如何帮助我们定义类型约束的,从而使代码更加类型安全和易于维护。C++20的概念特性为模板编程带来了极大的便利,使得我们可以编写出更加健壮和灵活的代码。

#CPlusPlus #C++20 #Concepts #编程技巧 #代码质量 #模板编程 #类型安全 #实战示例


http://www.ppmy.cn/devtools/110127.html

相关文章

Grafana进阶教程:使用Loki、Tempo进行日志与追踪可视化

Grafana 进阶教程&#xff1a;使用 Loki、Tempo 进行日志与追踪可视化 在现代运维和开发环境中&#xff0c;日志和追踪是观测系统健康状态、分析问题和优化性能的重要手段。Grafana 是一款广泛使用的开源数据可视化和监控平台&#xff0c;它支持与多种数据源的集成&#xff0c…

Qt对话框布局调整

Qt 基础: 在"main.cpp" 文件的开始部分加入以下头文件&#xff1a; #include<Qsplitter> #include<QTextEdit> #include<QTextCodec> 停靠窗口QDockWidget 类: 停靠窗口QDockWidget 类也是在应用程序中经常用到的&#xff0c;设置停靠窗口的…

import.meta.glob 自动读取路由

src/router/index.js import { createRouter, createWebHistory } from vue-router const initRouteModules async () > {// vite提供的批量导入文件方法&#xff0c;导入模块路由// 需要在应用加载时就预先加载所有匹配到的模块&#xff0c;以便加快应用的启动速度{ eage…

LeetCode 热题100-64 搜索二维矩阵

搜索二维矩阵 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c…

基于MATLAB的图像融合设计

摘 要 图像融合能够将不同类型传感器获取的同一对象的图像数据进行空间配准。并且采用一定的算法将不同类型的传感器获取的同一对象的图像数据所含用的信息优势或互补性有机地结合起来产生的新的图像数据。这种新数据含有所研究对象的更多信息表征&#xff0c;与单一图像相对比…

Gin框架中的全局中间件与中间件传值

Gin是一个用Go语言编写的Web框架&#xff0c;它以高性能和简洁的API而闻名。在Gin中&#xff0c;中间件是一种在请求处理流程中执行特定任务的函数&#xff0c;这些任务可能包括日志记录、用户认证、请求限流等。中间件可以在全局范围内使用&#xff0c;也可以仅应用于特定的路…

nacos 高级 配置管理 动态路由

一、配置共享 1.介绍 在nacos中配置共享文件可以简化微服务中各个配置文件的书写&#xff0c;规范文件 Nacos 配置共享是一种在多个服务中共享相同配置的技术&#xff0c;其目的在于减少重复配置的维护量&#xff0c;提升配置管理的效率和一致性。 实现Nacos配置共享&#xf…

18个提升邮件打开率、点击率和销售额的营销方案

你知道电子邮件营销是一种非常有效的方式&#xff0c;可以培养潜在客户&#xff0c;将他们转化为付费客户&#xff0c;并保留 现有 客户。 但要实现你的目标&#xff0c;需要持续且频繁地发送电子邮件——可能至少每周一次。而想出这么多电子邮件营销活动创意是 很难的。 也许…

十一、MySQL高级—工具和技巧拾遗~视图 VIEW(4)

&#x1f33b;&#x1f33b; 目录 一、是什么二、作用三、适用场景四、语法五、注意事项(适用5.5) 文章大纲 &#x1f447;&#x1f447; 一、是什么 将一段查询sql封装为一个虚拟的表。 这个虚拟表只保存了sql逻辑&#xff0c;不会保存任何查询结果。 二、作用 1、封装复杂sql…

Docker绑定端口后仍无法远程直接访问

在docker中拉取镜像后启动&#xff0c;启动脚本如下 启动docker docker run --name nacos-server \ -p 8848:8848 \ -p 7848:7848 \ -p 9848:9848 \ -p 9849:9849 \ --privilegedtrue \ --restartalways \ -e JVM_XMS256m \ -e JVM_XMX256m \ -e MODEstandalone \ -e PREFER_…

jmeter压力测试,通过LLM利用RAG实现知识库问答,NEO4J部署,GraphRAG以知识图谱在查询时增强提示实现更准确的知识库问答(9/7)

前言 这周也是杂七杂八的一天&#xff08;高情商&#xff1a;我是一块砖&#xff0c;哪里需要往哪里搬&#xff09;&#xff0c;首先是接触了jemter这个压力测试工具&#xff0c;然后帮公司的AIGC项目编写使用手册和问答手册的第一版&#xff0c;并通过这个平台的智能体实现知识…

MFC生成dll的区别

主要分三种&#xff1a; A. 动态链接库(dll) B.具有导出项的(dll)动态链接库 C.MFC动态链接库 对比项目&#xff1a;可以根据需要选择哪种dll方便 添加自定义导出功能Demo 1. 添加导出实现接口&#xff1a; A. 导出需要具有&#xff1a;__declspec(dllexport) B. 按照C语言…

Windows10 如何配置python IDE

Windows10 如何配置python IDE 前言Python直接安装&#xff08;快速上手&#xff09;Step1.找到网址Step2.选择版本&#xff08;非常重要&#xff09;Step3. 安装过程Step4. python测试 Anaconda安装&#xff08;推荐&#xff0c;集成了Spyder和Pycharm的安装&#xff09;Step1…

计算机网络 第3章 数据链路层

文章目录 数据链路层基本概念数据链路层功能概述封装成帧和透明传输差错检测奇偶校验码循环冗余码CRC 可靠传输停止-等待协议SW&#xff08;Stop and Wait&#xff09;回退N帧协议GBN&#xff08;GO Back N&#xff09;选择重传协议SR&#xff08;Selective Request&#xff09…

5G毫米波阵列天线仿真——CDF计算(方法一)

累计分布函数&#xff08;CDF&#xff09;在统计学上是一个由0增长到1的曲线。5G中CDF被3GPP标准推荐使用&#xff0c;5G 天线阵的有效全向辐射功率EIRP的CDF函数被用来评价设备的质量和性能。由于EIRP是在某一个方向角theta, phi上的辐射功率&#xff0c;幅值由天线增益与激励…

css 动态宽度的同时高度自适应(含内容居中)

html内容 <div classcontent><span>我是内容</span></div>自适应高度 此时内容将无法居中 .content { width: 100%; /* 或者其他任意值 */height: 0; padding-top: 100%; /* 与width相等 */ }居中内容 .content { width: 100%; /* 或者其他任意值 …

RP2040 C SDK clocks时钟源配置使用

RP2040 C SDK clocks时钟源配置使用 &#x1f33f;RP2040时钟源API函数文档&#xff1a;https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_clocks &#x1f341;RP2040时钟树&#xff1a; 系统时钟源可以来自外部时钟输入&#xff08;exte…

linux系统下PostgreSQL的使用

文章目录 前言一、安装pgsql数据库二、安装c和c驱动三、使用1、头文件2、源文件3、main文件4、编译 前言 最近工作中使用到了pgsql,主要是使用其c驱动完成数据库创建及增删改查等操作… 一、安装pgsql数据库 使用命令如下: sudo apt-get install postgresql安装完成,使用如…

C++ ─── List的模拟实现

目录 ​编辑 一&#xff0c; List的模拟实现 二&#xff0c;代码实现 三、list和vector的区别 一&#xff0c; List的模拟实现 List 是一个双向循环链表,由于List的节点不连续&#xff0c;不能用节点指针直接作为迭代器&#xff0c;因此我们要对结点指针封装&#xff0c;来…

Delphi 12.1安卓APP开发中获取硬件信息及手机号

Demo与代码已上传到CSDN下载。 这里简单说一下代码内容&#xff0c;完整代码请自行下载&#xff0c;不清楚的欢迎留言交流。 前言 演示Demo使用了我自己开发的一个控件&#xff0c;TLayoutPro 《Delphi D10.3 LayoutsPro 控件简介 -避免输入焦点被虚拟键盘遮挡》请查看并下载控…