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

C++ 20: Concepts 与Requires

1. 简介

C++的泛型编程使得一个函数可以对不同参数类型的进行使用,

但有时候参数类型不对的时候就有一大堆的报错,好像跟SFIANE这个

特性有关,没有仔细研究过。

我自己的理解Concepts就是对模板类型的参数进行一个限制的东西,

这样在编译期间我们就可以知道哪里可能出错了。

2. Conceps使用例子

下面的代码就是一个例子,来求最大公约数的。

我们直接使用标准库里面的std::is_integral来判断。

#include <iostream>
#include <type_traits>
#include <iostream>
template <typename T>
concept Integral = std::is_integral<T>::value;Integral auto gcd(Integral auto a, Integral auto b)
{if (b == 0) return a;else return gcd(b, a % b);
}int main()
{auto g1 = gcd(10,5);// auto g2 = gcd(10.1,2.2);std::cout << g1 << std::endl;return 0;
}

如果我们把g2的注释就会直接报错说类型不匹配

在这里插入图片描述

而如果我们不使用concept进行限制的话

template<typename T>
T gcd(T a, T b)
{return b == 0 ? a : gcd(b, a % b);
}

错误就会在实例化期间才会被捕捉到。
在这里插入图片描述
上面的concepts的其实是加了语法糖的,它的原始形式是下面的这样。

requires就是表示这个模板参数需要满足concept所对应的限制。

template<typename T>
requires Integral<T>
T gcd(T a, T b)
{return b == 0 ? a : gcd(b, a % b);
}

定义concepts的语法格式

template <template-parameter-list>
concept concept-name = constraint-expression;

限制的表达式大体分两种

  • 通过与&&||和非!进行连接的

限制表达式是一个在编译期就可以决定的bool值,

下面例子就是来判断,是不是有符号整数和无符号整数。

// SignedUnsignedIntegrals.cpp#include <iostream>template <typename T>
concept Integral = std::is_integral<T>::value;template <typename T>
concept SignedIntegral = Integral<T> && std::is_signed<T>::value;template <typename T>
concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;void func(SignedIntegral auto integ) {               // (1)std::cout << "SignedIntegral: " << integ << '\n';
}void func(UnsignedIntegral auto integ) {             // (2)std::cout << "UnsignedIntegral: " << integ << '\n';
}int main() {std::cout << '\n';func(-5);func(5u);std::cout << '\n';}

另外 一种就是requires表达式

3. 使用requires定义concept

我们先来看下requires的语法格式

requires (parameter-list(optional)) {requirement-seq} 

requires总共分四种类形的限制

  • 简单限制
  • 类型限制
  • 复合限制
  • 内嵌限制
3.1 简单限制
template<typename T>
concept Addable = requires (T a, T b) {a + b;
};
3.2 类型限制

类型它有没有相应的变量名,有没有办法构造另外一个对象。

#include <iostream>
#include <vector>template <typename>
struct Other;  template <>
struct Other<std::vector<int>> {};template<typename T>
concept TypeRequirement = requires {typename T::value_type;             // (2)typename Other<T>;                  // (3)
};                         int main() {TypeRequirement auto myVec= std::vector<int>{1, 2, 3};  // (1)}
3.3 复合限制

语法格式

{expression} noexcept(optional) 
return-type-requirement(optional);    

下面就是一个例子,定义了相等这个概念,它要求对象的!===

运算符最终都可以转换为bool值。但对于WithoutEqualWithoutUnEqual它们缺失了一个,因此就会报错。

#include <concepts>
#include <iostream>template<typename T>                                     // (1)
concept Equal = requires(T a, T b) {{ a == b } -> std::convertible_to<bool>;{ a != b } -> std::convertible_to<bool>;
};bool areEqual(Equal auto a, Equal auto b){return a == b;
}struct WithoutEqual{                                       // (2)bool operator==(const WithoutEqual& other) = delete;
};struct WithoutUnequal{                                     // (3)bool operator!=(const WithoutUnequal& other) = delete;
};int main() {std::cout << std::boolalpha << '\n';std::cout << "areEqual(1, 5): " << areEqual(1, 5) << '\n';/*bool res = areEqual(WithoutEqual(),  WithoutEqual());    // (4)bool res2 = areEqual(WithoutUnequal(),  WithoutUnequal());*/std::cout << '\n';}

同样再举例

template<class T>
concept TCPDatagramAdapter = requires( T a, TCPMessage seg ) {{ a.write( seg ) } -> std::same_as<void>;{ a.read() } -> std::same_as<std::optional<TCPMessage>>;
};

这个adapter就要求a.write( seg )的返回值为空,且读取的返回值是std::optional<TCPMessage>

3.4 内嵌限制

就是在定义concept时,加在后面,比如说下面的代码;

用了两种方式来定义UnsignedIntegral这个概念,

后面一种就是用了内嵌的require,但是这里不使用内嵌可能

可读性更好,当然这只是为了举例。

// nestedRequirements.cpp#include <type_traits>template <typename T>
concept Integral = std::is_integral<T>::value;template <typename T>
concept SignedIntegral = Integral<T> && std::is_signed<T>::value;// template <typename T>                               // (2)
// concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;template <typename T>                                  // (1)
concept UnsignedIntegral = Integral<T> &&
requires(T) {requires !SignedIntegral<T>;
};int main() {UnsignedIntegral auto n = 5u;  // works// UnsignedIntegral auto m = 5;   // compile time error, 5 is a signed literal}

4. 参考

主要翻译或者是抄得

modercpp-concepts-requires
modercpp-defining-concept

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

相关文章:

  • 链表-23.合并K个升序链表-力扣(LeetCode)
  • Qt从qmake迁移到cmake的记录
  • Spring Boot 整合网易163邮箱发送邮件实现找回密码功能
  • PHP - 线程安全 - 疑问与答案
  • PyQt6 进阶篇:构建现代化、功能强大的桌面应用
  • uniApp对接实人认证
  • Clustering Enabled Wireless Channel Modeling Using Big Data Algorithms
  • 【前端debug调试】
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘arviz’问题
  • 网站速度慢?安全防护弱?EdgeOne免费套餐一次性解决两大痛点
  • chapter05_从spring.xml读取Bean
  • 完整实验命令解析:从集群搭建到负载均衡配置
  • Java:类及方法常见规约
  • Unity中删除不及时的问题
  • 牛客面经2 京东社招-002
  • PyTorch框架之图像识别模型与训练策略
  • 25.深入对象
  • 寻找AI——高保真还原设计图生成App页面
  • 华为/思科/H3C/锐捷操作系统操作指南
  • 鸿蒙应用网络开发实战:HTTP、WebSocket、文件下载与网络检测全攻略
  • 微信小程序和uni-app面试问题总结
  • 网络模型深度解析:CNI、Pod通信与NetworkPolicy
  • Spring Boot 实时广播消息
  • Java集合(Collection、Map、转换)
  • git实战(7)git常用命令速查表
  • GitHub发布革命性工具:GitHub Spark,用自然语言打造全栈智能应用
  • 商品与股指类ETF期权买卖五档Tick分钟级历史行情数据分析
  • Node.js 和 Express 面试问题总结
  • 目标跟踪 YOLO11 单目标跟踪
  • Maven仓库与Maven私服架构