C++ 20 Concept 语法

2023-05-16

requires expression

一种表达式,它很像一个lambda表达式,一个未命名元函数。例如:

requires(int a,int b){ a+b;}

其中:()部分是参数列表,{ }部分是需求列表。这个表达式是在编译期求值的,表达式的结果是true或者false。它的工作机制是:对于{ }内的若干条语句检查可行性,如果都能通过,则本条requires expression的值为true。在本例中,a+b;是提出了需求,即:有关两个int要求能相加。显然是满足的。则本例的表达式值为true。

int main(){
    static_assert( requires(int a,int b){ a+b;} );
}

这个静态断言,证明了此种表示式是编译期求值的特性。

int main(){
    static_assert( requires{ false; } );  //断言仍正确
}

上述语句,可以看出()部分是可选的,因此可省略。{ }部分中false;这个表达式是可行的,记住我们不关心被检查的表达式的结果,只关心它是否可行。{ }中是列举需求的,我们有四种需求描述方法:1)简单需求 2)类型需求 3)复杂需求 4)嵌套需求。 
前文的a+b就是个简单需求。另外一个简单需求例子:

int main(){
    static_assert( requires{ new int; } );  //OK
}

而当我们不但要检查表达式是否可行,还要检查表达式是否抛例外,需要用到复杂需求形式。可以像这样写:

int main(){
    static_assert( requires{ (new int) noexcept; );  //ERROR
}

显然,new int是抛例外的,我们却需求不跑例外,需求不满足,requires表达式求值为false,则静态断言失败。 当我们需要表达式的值的类型符合某种要求时,这样写:

#include <type_traits>
int main(){
    static_assert( 
        requires{  {new int} -> std::same_as<int*>;  }  
   );
}

下一个是类型需求(Type Requirement)的例子

#include <vector>
int main(){
    static_assert( requires{ typename std::vector<int>::iterator; } );
}

类型需求的标志就是typename,平铺直叙,直截了当。我们看到std::vector<int>::iterator这个内嵌类型是存在的。
下一个例子:

#include <type_traits>
#include <vector>

int main(){
    static_assert( 
        requires(std::vector<int> v, int i){  
            v.at(i);  //Simple Requirement
            typename std::vector<int>::value_type;   //Type Requirement
            sizeof(int)==5;  //? 居然检查通过了
        }  
   );
}

看到sizeof(int)==5检查通过了,这是当然的,还记得Simple Requirements只检查表达式可行性? 如果要检查表达式的值,则需要使用Nest Requirements语法:

#include <type_traits>
#include <vector>

int main(){
    static_assert( 
        requires(std::vector<int> v, int i){  
            v.at(i);  //Simple Requirement
            typename std::vector<int>::value_type;   //Type Requirement
            requires (sizeof(int)==4);  //Nest Requirement
        }  
   );
}

requires clause

C++对关键字充分榨取价值,往往一个关键字在多个场合使用。此处使用的是需求从句场合。用于限定模板函数的类型参数。

template<typename T> 
requires true
T add(T a, T b) { 
    return a + b; 
}

基本原理是,requires 之后一个常量表达式,当此表达式为true时,模板函数被选用。requires true恒成立,相当于对T没有限制。clause表达式可以使用的操作数为:1)基本表达式

template<typename T> 
requires std::is_integral<T>::value  //基本表达式
T add(T a, T b) { 
    return a + b; 
}

什么是“基本表达式”(primary expression)? 文字量,标识符,fold表达式,还有requires表达式等,这些都是基本的。反例,2>1 就不是基本表达式,因为这个表达式可以分解为一个运算符> ,配合两个基本表达式1和2。所以:

template<typename T> 
requires 2>1   //语法错误
T add(T a, T b) { 
    return a + b; 
}

requires 2>1这个从句就是语法错误的。解决办法是加括号,括号内的是基本表达式。例如:

template<typename T> 
requires (2>1)  //括号括起来,作为一个整体的基本表达式
T add(T a, T b) { 
    return a + b; 
}
#include <type_traits>

template<typename... Args> 
requires (std::is_integral_v<Args> && ...)   //折叠表达式 fold expression
int add(Args... a) { 
    return (a + ...);
}

int main(){
    
    int a=1;
    short b=2;
    char c=3;
    
    return add(a,b,c);
}
template<typename T> 
requires requires(T a){ ++a; }  //require表达式
T add(T a) { 
    return ++a;
}

int main(){
    add(false);  //自C++17开始,bool型无++运算,因此触发constraint报警
}

requires从句的constraint表达式部分,可以用逻辑运算符把基本表达式组合起来,例如:

#include <type_traits>

template<typename T> 
requires std::is_integral_v<T> && ( sizeof(T)>=4 )
T add(T a) { 
    return ++a;
}

int main(){
    short v=1;  //因为short长度2字节,不符合>=4字节的这条限制,故触发报警
    add(v);
}

constraint-expression

此表达式的基本表达式必须是返回bool纯右值(true或false),这些基本表达式用逻辑运算符连接。往往这个表达式是泛型的,当它生存在模板类或模板函数定义上下文中,就会出现模板的类型参数。

concept

终于到达了这个高级概念。前面说了constraint-expression用于表达对于类型应该张什么样子的需求描述。现在我们把这个需求集合整理起来,给起一个名字。这个机制就是C++20的concept机制。concept就是类型需求说明书。

#include <type_traits>

template<typename T>
concept My_First_Type_Requirement_Specification =   //我的第一个类型需求规格说明书
    std::is_integral_v<T> &&   //第一条:必选是整形
    ( sizeof(T)>=4 ) &&        //第二条:类型尺寸不小于4
    requires(T a){ ++a; }      //第三条:类型支持自++运算符。
;

template<typename T> 
requires My_First_Type_Requirement_Specification<T>    //requires从句
T add(T a) { 
    return ++a;
}

int main(){
    short v=1;  //因为short长度2字节,不符合>=4字节的这条限制,故触发报警
    add(v);
}

concept可以看做可重用的元函数,我可以只在一处修改这个元函数,其它引用此元函数的地方都不必一一修改了。而requires表达式更像一个lambda表达式,用于ad-hoc用途(拉丁语:尽在此处使用),避免了先在很远的地方定义一个元函数,然后才能在使用的尴尬。

#include <type_traits>
#include <concepts> 

//std::integral<T>是C++20 concept库中定义的一个东西,目的是不要大家重复造轮子。
//即一个简单的:template < class T > concept integral = std::is_integral_v<T>;

//requires-clause中使用concept。把requires true,替换为requires std::integral<T>
template<typename T> 
requires std::integral<T>    //constaint-expression, 要求std::integral<T>实例化为true
T add(T a) { 
    return ++a;
}
 
//把concept放到typename位置,要求concept是一元的谓词元函数,即C<T>形式
template<std::integral T> 
T add(T a, T b) { 
    return a+b;
}

template<typename V,typename U>
concept binary_concept = true;

//ERROR: binary_concept是二元的谓词,不能这么写
template<binary_concept T> //binary_concept<T>,不符合binary_concept<V,U>
void add2(T a, T b) { 
    return a+b;
}

int main(){
    
    static_assert(std::integral<int>);//static_assert(true), 把true替换走
    
    //requires表达式中使用concept,就像表达式中调函数的意思。例如:(x + f(2))*10
    static_assert( requires(){ 
                      std::integral<int> ;  //调concept
                   } );
    
}

上面例子是concept一般可能出现的地方。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 20 Concept 语法 的相关文章

  • 创建表时附带的ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic 的解释

    ENGINE 61 InnoDB DEFAULT CHARSET 61 utf8 COLLATE 61 utf8 general ci ROW FORMAT的解释 1 示例 CREATE TABLE 96 student 96 96 id
  • c语言编程“水仙花数”

    文章目录 打印所有的水仙花数 所谓的 水仙花数 是指一个三位数 xff0c 其各位数字的立方和等于该数本身 例如 xff0c 153是水仙花数 xff0c 因为153 61 1 3 43 5 3 43 3 3 打印所有的水仙花数 所谓的 水
  • inux查看日志的几种方法

    linux 日志查看 tail head cat tac sed less echo 1 命令格式 tail 必要参数 选择参数 文件 f 循环读取 q 不显示处理信息 v 显示详细的处理信息 c lt 数目 gt 显示的字节数 n lt
  • asp不能正常用的原因

    前几天做网站时 xff0c 机子出现了这种症状 xff0c 重装过IE和IIS一样也无法解决 xff0c 在百度里找了一下 xff0c 下面的方法真的很适用 症状 xff1a 运行asp程序 包括其他动态网页程序 出现500内部错误信息 x

随机推荐

  • 用DLL实现把数据库的记录导出到EXCEL中(VB)

    39 新建一个ActiveX DLL工程工程名为DbToExcel 39 工程 gt 引用 引用Microsoft ActiveX Data Objects 2 6 Library 39 Microsoft Excel 9 0 Object
  • MySQL转换为SqlServer数据库

    如何将MySQL数据导入到SqlServer中 xff0c 请看以下步骤 xff1a 1 安装mysql数据库的ODBC驱动 xff0c mysql connector odbc 3 51 19 win32 msi 2 打开控制面板管理工具
  • DataTimePicker数据绑定遇到Null时异常的原因

    DateTimePicker1 DataBindings Add 34 Value 34 bindingSource1 34 assessortime 34 如果字段 assessortime的值 为 null 时 就会出现异常 后来发现
  • c#中DataTable与实体集合相互转换

    以下是将集合类转换成DataTable lt summary gt 将集合类转换成DataTable lt summary gt lt param name 61 34 list 34 gt 集合 lt param gt lt return
  • 用Linux命令行生成随机密码的十种方法

    转载自 极客范 xff0c 不得不夸夸强大的Bash啊 xff01 xff01 xff01 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
  • C++20 Ranges

    VS2019 C 43 43 20的Ranges 01 引入范围的动机02 范围 ranges 03 range v3库04 C 43 43 20 range demo 01 引入范围的动机 C 43 43 17以前的标准库中大多数通用算法
  • 面向对象分析设计步骤

    一 创建用例 初步确定用例 xff1a 1 确定参与者 2 确定用例 xff08 系统操作 xff09 3 确定参与者与用例之间的关系 用例细节描述 xff1a 1 用例名称 2 操作详细描述 3 前置条件描述 4 部署约束 5 正常事件流
  • collect2: ld terminated with signal 9 错误解决办法

    编译android是出现如下错误 xff1a target Java CameraEffectsTests out target common obj APPS CameraEffectsTests intermediates classe
  • 浅谈Stein算法求最大公约数(GCD)的原理及简单应用

    一 Stein算法过程及其简单证明 1 一般步骤 xff1a s1 当两数均为偶数时将其同时除以2至至少一数为奇数为止 xff0c 记录除掉的所有公因数2的乘积k xff1b s2 如果仍有一数为偶数 xff0c 连续除以2直至该数为奇数为
  • 【已解决】@Configration爆红

    64 Configration爆红 问题原因 xff1a 一 xff1a 没有添加依赖 二 xff1a 添加依赖了 xff0c 但是依赖版本过低 解决方法 xff1a 把依赖的版本改的高一点 span class token generic
  • 关于冒泡排序的程序( 第三次作业)

    此前想过把两种排序方式都一起写在一个工程文件里 xff0c 但做了下 xff0c 能力有限 xff0c 没法写完整 xff0c 所以就只能分别写 xff0c 这个是冒泡排序 xff0c 代码已尽量做到准确的注释 xff0c 希望提醒自己往后
  • BSS段

    深入理解计算机系统 bss段 xff0c data段 text段 堆 heap 和栈 stack 1 关于BSS段的大小 2 1 BSS段中的内容 2 2 BSS段在加载运行前的处理 3 3 BSS段的作用 3 4 代码优化对BSS段的影响
  • Java 比较两个List对象差集(根据某一值)

    很多都是比较List lt String gt 的 xff0c 和自身业务不符 xff0c jdk1 8 新特性强大的Stream API xff0c 具体是什么方法 xff0c 什么作用自行百度 xff0c 复制粘贴可以解决问题就OK 4
  • Windows10 安装Redis(图文教程)

    Redis xff08 Remote Dictionary Server xff0c 即远程字典服务 xff0c 是一个开源的使用ANSI C语言编写 支持网络 可基于内存亦可持久化的日志型 Key Value数据库 一 下载redis客户
  • e17 enlightenment 介绍及配置

    为什么要有一个窗口管理器 为什么一定要有一个桌面背景 xff0c 甚至是标题栏 或是如果把一个应用程序如firefox当成桌面背景行不行 桌面能不能再快一点 我不想把资源浪费在那些用不到的地方 Linux那么多虚拟桌面 xff0c 为什么我
  • Vim: Warning: input is not from a terminal 后退出 vim 终端异常

    Vim Warning input is not from a terminal 后退出 vim 终端异常 今天执行了如下命令调用 vi 来打开 find 搜索到的文件 xff1a longyu 64 longyu pc span clas
  • UNPV2 学习:Posix Message Queues

    文章目录 特点消息队列的释放mq notify 函数mq notify 使用信号通知消息到达直接在信号处理函数中调用 mq notify 与 mq receive 函数来接收数据在信号处理函数中设置标志在程序主逻辑中调用 mq notify
  • VMware ESXI虚拟机磁盘在线扩容后fdisk -l 找不到问题解决

    VMware ESXI虚拟机磁盘在线扩容后fdisk l 找不到问题解决 在VMware ESXI终端页面为虚拟机新增磁盘后 xff0c 进入虚拟机执行fdisk l 找不到新增的盘 重启系统肯定是可以解决的 xff0c 但是机器有在跑测试
  • go调用python

    安装 安装python和go的环境 xff0c 在debian和ubuntu系统上 xff0c 还要sudo apt install python all dev安装sudo apt get install pkg config安装go g
  • C++ 20 Concept 语法

    requires expression 一种表达式 xff0c 它很像一个lambda表达式 xff0c 一个未命名元函数 例如 xff1a requires int a int b a 43 b 其中 xff1a xff08 xff09