C++雾中风景18:C++20, 从concept开始
转眼间,C++20的标准已经发布快两年了。不少C++的开源项目也已经将标准升级到最新的C++20了,笔者也开启了新标准的学习历程了。所以借这系列的博文,记录下笔者学习新标准的一些心得与吐槽~~
作为C++20系列的第一篇开篇之文,就要从千呼万唤始处理的concept
聊起了,后续很多新的feature的实现,也仰赖新的concept
的实现,后续笔者的文章也会逐步展开。OK,开始我们C++20旅程的第一站:concept
1.First Look
先从一个群友的一个实际的问题出发,我们来看看concept
可以解决什么问题。是怎么样通过coding实现的。
- SFINAE
熟悉C++模板编程的小伙伴肯定第一时间想到通过SFINAE的方式来解决,让笔者来解决这个问题的话,会写出下面的代码:
template <typename T>
T test(T a) {
static_assert(!std::is_same_v<void, decltype(a + a)>);
static_assert(!std::is_same_v<void, decltype(a - a)>);
static_assert(!std::is_same_v<void, decltype(a * a)>);
static_assert(!std::is_same_v<void, decltype(a / a)>);
return a;
}
这里写的代码是一个略微Trick的表达,利用decltype
去获取操作符计算后的类型,然后用std::is_same_v
进行一个其实没什么意义的类型比较,来满足static_assert
的语义,最终满足我们对模板类型T
的一些约束。
- concept
显然上面的方式是很不直观的,虽然能达到咱们的目的,但是从代码优雅角度来说是一种较差的选择实现。
我们来看一下用C++20提供给我们的Concept是如何解决这个问题的:
template <typename T>
concept Cal = requires (T a) {
a + a;
a - a;
a * a;
a / a;
};
template <typename T>
requires Cal<T>
T test(T a) {
return a;
}
这是通过concept
来实现的一个类型约束方式,Cal
代表着一个concept的实现,requires
中花括号的内容就代表了对于类型T
的约束,要满足下面的操作符
a + a;
a - a;
a * a;
a / a;
Bingo! 似乎C++20给了我们一个更好的trait,接着往下看,我们继续来细探Concept的实现。
2. How to use
- concept的定义
这里写了一个例子,咱们基于这个例子来展开:
template <typename T>
concept Cal = requires (T a) {
a + a;
typename T::type;
{a + a} -> std::same_as<float>;
require Concept2<T>;
};
这里定义了4个requires的要求,只有满足这4个条件才能通过concept
的限制,正常进行编译。
1). a + a
这个是最简单的,该表达式能通过编译则代表符合要求,这里不会进行实际的计算。
2). typename T::type
代表需要,T
类型定义了type
类型,才符合要求
3). {a + a} -> std::same_as<float>
这里的{}
代表了decltype(a + a)
之后的类型需要满足后面的concept
的需求。这是一个语法糖,也可以通过这样来实现:requires std::is_floating_point_v<decltype(a + a)>
4). 最后的require Concept2<T>
这代表了concept
的嵌套逻辑。requires
后面可以带任意的concept
- concept的使用
了解了concept
定义之后,我们就可以利用concept
来进行模板类型的约束了。
这里“回字有四种写法”,大家可以选择自己喜欢的方式来使用。(真搞不懂搞这么多写法干什么,不能统一一下吗?, 下面介绍的顺序随笔者的偏好以此递减)
template<typename T>
requires Cal<T>
T test(T a)
{
return a + a;
}
1). 这是笔者最认可的一种书写方式,语义明确,在模板类型定义之后明确对它的要求。
template<Cal T>
T test(T a)
{
return a + a;
}
2). 也还行吧,模板参数前指定了concept
,也比较明确直观。
Cal test(Cal a)
{
return a + a;
}
3). concept
命名一长就显得有些琐碎了,并且把concept
当类型使用了,看起来有些怪异了。
template<typename T>
T test(T a) requires Cal<T>
{
return a + a;
}
4). 把concept
写后面的都是异端,当然如果你喜欢的话,也ok。
3. concept的本质
concept本质上是:一个可以套用在模板上的constexpr bool
值。
相信下面这段代码能帮助你更好的理解它:
template <typename T>
concept Con = std::is_floating_point_v<T>;
int main(int argc, char* argv[]) {
static_assert(std::is_same_v<decltype(Con<float>), bool>);
std::cout << Con<int> << std::endl;
return 0;
}
显然,上面的代码我们用is_same_v
确定了concept
生成的类型就是bool
类型。而同样的,在运行期,咱们也可以将concept
的结果作为一个bool
常量进行使用,并打印。
所以,take it easy。 concept
很简单,它只是C++20给你提供的一个better的工具,来摆脱被疯狂的模板报错所支配的恐惧。但即使你完全不了解它,使用老的方式,依然能够同样解决问题。
4.小结
C++的一些模板推断的错误常常让人抓狂。而很多时候我们使用它需要
- 要进行模板推断类型的编程设计
- 利用SFINAE的方式来类型约束
这无形之中增加Coding时的心智成本,而concept
作为一个新的语法糖,给了我们拆分二者的机会:让上帝归上帝,凯撒归凯撒。
使用好concept
来进行类型约束,enjoy新标准带来的便利吧。
希望大家能够有所收获,笔者水平有限。成文之处难免有理解谬误之处,欢迎大家多多讨论,指教。
5.参考资料
C++雾中风景18:C++20, 从concept开始的更多相关文章
- [网站公告]18:07-18:20阿里云SLB故障造成网站不能正常访问
(注:由于阿里云SLB管理控制台监控数据不准,实际故障时间是18:07-18:20.) 17:55-18:2018:07-18:20,我们使用的阿里云SLB(负载均衡)中有3台出现突发故障,造成全站无 ...
- 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
// test20.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...
- [18/11/20]break与continue的区别
一.普通break 和continue 1.break: break用于强行退出循环,不执行循环中剩余的语句. 2.continue continue 语句用在循环语句体中,用于终止某次循环过程,即跳 ...
- Windows phone应用开发[18]-下拉刷新
在windows phone 中采用数据列表时为了保证用户体验常遇到加载数据的问题.这个问题普遍到只要你用到数据列表就要早晚面对这个问题. 很多人会说这个问题已经有解决方案. 其实真正问题并不在于如何 ...
- 18位身份证验证--java实现,正则表达式
简单的正则表达式: (1)preg_match("/^(\d{18,18}|\d{15,15}|\d{17,17}x)$/",$id_card)(2)preg_match(&quo ...
- Visual Studio原生开发的20条调试技巧(下)
我的上篇文章<Vistual Studio原生开发的10个调试技巧>引发了很多人的兴趣,所以我决定跟大家分享更多的调试技巧.接下来你又能看到一些对于原生应用程序的很有帮助的调试技巧(接着上 ...
- [CareerCup] 18.4 Count Number of Two 统计数字2的个数
18.4 Write a method to count the number of 2s between 0 and n. 这道题给了我们一个整数n,让我们求[0,n]区间内所有2出现的个数,比如如 ...
- 18.用两个栈实现队列[2StacksToImplementQueue]
[题目] 某队列的声明如下: C++ Code 123456789101112131415 template<typename T> class CQueue { public: ...
- 对Linux新手非常有用的 20个命令
你打算从Windows换到Linux上来,还是你刚好换到Linux上来?哎哟!!!我说什么呢,是什么原因你就出现我的世界里了.从我以往的经验来说,当我刚使用Linux,命令,终端啊什么的,吓了我一跳. ...
随机推荐
- 洛谷P2240 【深基12.例1】部分背包问题(经典模板)
首先应该注意几个关键信息:装走尽多的金币.所有金币可以随意分割并且分割后金币的价值比不变; 我们可以抽象成一个买东西的场景,给你一定数目的钱,让你在这些钱内尽量在超市买更多的小零食,这就牵扯到了一个性 ...
- js null和{}区别
{}是一个不完全空的对象,因为他的原型链上还有Object呢,而null就是完全空的对象,啥也没有,原型链也没有,所以null instanceof Object === false;[]就更不用说了 ...
- 2022寒假集训day2
day1:学习seach和回溯,初步了解. day2:深度优化搜索 T1 洛谷P157:https://www.luogu.com.cn/problem/P1157 题目描述 排列与组合是常用的数学方 ...
- 类(静态)变量和类(静态)static方法以及main方法、代码块,final方法的使用,单例设计模式
类的加载:时间 1.创建对象实例(new 一个新对象时) 2.创建子类对象实例,父类也会被加载 3.使用类的静态成员时(静态属性,静态方法) 一.static 静态变量:类变量,静态属性(会被该类的所 ...
- suse 12 二进制部署 Kubernetets 1.19.7 - 第04章 - 部署docker服务
文章目录 1.4.部署docker 1.4.0.下载docker二进制文件 1.4.1.配置docker镜像加速 1.4.2.配置docker为systemctl管理 1.4.3.启动docker服务 ...
- FutureTask类的get方法如何实现线程同步等待
接上篇JDK中线程中实现同步等待闭环的一种方式 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com),为什么使用了FutureTask中的get方法就可以实现线程的同步等待?这就将重点讲述下F ...
- 关于TP90 TP99 等常用于评估软件系统的处理性能的指标概念
工作中还是蛮少直接接触到评估系统性能的,但是不妨碍有兴趣了解.认为这是常识,只是个人才疏学浅不了解其定义. TP=Top Percentile,Top百分数,是一个统计学里的术语,与平均数.中位数都是 ...
- 分析CVE-2018-18557与复现
前言 cve描述: LibTIFF 4.0.9 (with JBIG enabled) decodes arbitrarily-sized JBIG into a buffer, ignoring t ...
- 解决Springboot中的日期解析错误
错误信息: error: Failed to parse Date value '2022-01-12 15:00:00': Cannot parse date "2022-01-12 15 ...
- Java泛型的那些事
1.泛型概述 1.1.为什么使用泛型 没有泛型,在编写代码时只能使用具体类型或Object类型,无法做到使用者想要使用什么类型就是类型.比如:创建一个方法,形参需要指定需要使用的数据类型,在创建方法之 ...