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 easyconcept很简单,它只是C++20给你提供的一个better的工具,来摆脱被疯狂的模板报错所支配的恐惧。但即使你完全不了解它,使用老的方式,依然能够同样解决问题。

4.小结

C++的一些模板推断的错误常常让人抓狂。而很多时候我们使用它需要

  • 要进行模板推断类型的编程设计
  • 利用SFINAE的方式来类型约束

这无形之中增加Coding时的心智成本,而concept作为一个新的语法糖,给了我们拆分二者的机会:让上帝归上帝,凯撒归凯撒
使用好concept来进行类型约束,enjoy新标准带来的便利吧。

希望大家能够有所收获,笔者水平有限。成文之处难免有理解谬误之处,欢迎大家多多讨论,指教。

5.参考资料

CppReference

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容