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

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.dtcms.com/a/346902.html

相关文章:

  • 告别SaaS数据绑架,拥抱数据主权:XK+独立部署版跨境商城定制,为海外物流企业深度赋能
  • CentOS创建管理员用户feixue并设置密码全教程
  • 【c++进阶系列】:万字详解多态
  • 快速掌握Java非线性数据结构:树(二叉树、平衡二叉树、多路平衡树)、堆、图【算法必备】
  • STM32学习笔记19-WDG
  • linux shell测试函数
  • 百度深度学习面试:batch_size的选择问题
  • Linux总线设备驱动模型深度理解
  • 玩转Vue3高级特性:Teleport、Suspense与自定义渲染
  • 内联函数是什么以及的优点和缺点
  • ICP语序文字点选验证逆向分析(含Py纯算源码)
  • 基于SpringBoot+vue校园点餐系统
  • 【升级版】从零到一训练一个 0.6B 的 MoE 大语言模型
  • RabbitMQ面试精讲 Day 28:Docker与Kubernetes部署实践
  • JAVA核心基础篇-枚举
  • 【Linux网络编程】分布式Json-RPC框架 - 项目设计
  • Java试题-选择题(16)
  • 2025年渗透测试面试题总结-29(题目+回答)
  • 基于ResNet50的血细胞图像分类模型训练全记录
  • 2025-08-23 李沐深度学习19——长短期记忆网络LSTM
  • LeetCode 448.找到所有数组中消失的数字
  • 力扣 第 463 场周赛
  • 两款快速启动软件下载及安装!(GeekDesk和Lucy)!可图标归类!桌面更简洁
  • eBay运营全链路解析:从售后风控到生命周期营销的效率革命
  • 软件测试从入门到精通:通用知识点+APP专项实战
  • 基于STM32设计的养殖场环境监测系统(华为云IOT)_267
  • 8月23日星期六今日早报简报微语报早读
  • 施工场景重型车辆检测识别数据集(挖掘机、自卸卡车、轮式装载机):近3k图像,yolo标注
  • 奇怪的前端面试题
  • UDP报文的数据结构