首页 > 编程语言 >c++20 模板约束

c++20 模板约束

时间:2024-03-12 11:56:47浏览次数:15  
标签:std concept 20 cout val c++ const requires 模板

concept

c++20 中,提案许久的 concept 被加入到标准中了,这也意味着不用再写恼人的 SFINAE 了(除非你是一个受虐狂,喜欢对着一堆报错中找到错误的位置)。

c++20 之前

c++20 之前,如果需要对模板实参进行编译期检查,只能使用 SFINAE ,或者是部分使用 c++17 添加的 if constexpr 进行编译期检查。

SFINAE : Substitution Failure Is Not An Error ,替换失败不是错误,也就是在对函数模板的重载决议中,如果在模板形参替换为显示指定的类型或者是推导出的类型失败时,那么这个推导将被丢弃,并不会导致最终的编译失败。

假设我们需要实现一个输出函数,这个函数使用 std::cout ,将任何传入的参数输出,我们先看看一个基础写法是怎么写的。

template <typename T>
auto WriteLine(const T &val) -> void {
    std::cout << val << '\n';
}

很简单,对吧?问题来了,我们要怎么知道 std::cout << val << '\n' 中是否可以将 T 输出?一个简单的方式是写入,然后编译,结果自然是观察编译是否会出错。然而,如果你真的这么做了,就会得到一长串的 operator<< 错误,这实际上是标志着所有的重载函数都无法匹配上你所输入的类型,编译器不知道匹配哪一个,所以全部报错了。

SFINAE 怎么写?我们现在需要请出 std::enable_ifstd::declval 两位佬了。std::enable_ifSFINAE 的核心,如果满足约束条件,那么将得到一个确定的类型,否则没有类型,使得模板实例化失败。现在我们看看该怎么写。

template <typename T>
auto WriteLine(const T &val) 
  -> typename std::enable_if<std::is_same<decltype(std::cout << val), std::ostream&>::value, void>::type {
    std::cout << val << '\n';
}

哦天,后面一长串是什么?这里面使用了 std::is_same 判断两个类型是否是一样的,然后使用 decltype 推断 std::cout << val 的返回类型。由于 std::cout << val 输出后的返回类型为 std::ostream& ,如果它能够有匹配的输出,那么最终一定会成功,std::enable_ifvoid 作为 type ,否则这里 std::is_same 实例化将失败,也带着 std::enable_if 实例化失败,最终使得没有匹配的 WriteLine

如果你现在编译一下,会发现现在报错不会太多了,那么这解决我们需要的编译报错问题了吗?解决了,但是你需要花费巨大心智在模板上雕出一朵花,更不用说这只是一个简单的场景了,如果这个函数具有几个模板形参,每个参数都需要很多个约束类型,最终 SFINAE 会往可读性的地狱发展。

concept 和 requires

c++20 及其以后,我们可以不用再写 SFINAE 的代码形式了,只需要考虑 conceptrequires

我们使用这两个来对刚刚的丑陋东西重写一遍。

template <typename T>
  requires requires(const T val) { std::cout << val; }
auto WriteLine(const T &val) -> void {
    std::cout << val << '\n';
}

至少可读性好多了,上面的 std::enable_if 很难看出与 std::cout << val 的关系,可是 concept 呢?

template <typename T>
concept OutputAble = requires(const T val) {
    std::cout << val;
};

template <typename T>
    requires OutputAble<T>
auto WriteLine(const T &val) -> void {
    std::cout << val << '\n';
}

然而实际上,这样还是多余了点,我们可以更简单点。

template <OutputAble T>
auto WriteLine(const T &val) -> void {
    std::cout << val << '\n';
}

这样是不是直接看懂了模板需要的类型满足是什么了,对吧?但是这还能再简单。

auto WriteLine(const OutputAble auto &val) -> void {
    std::cout << val << '\n';
}

直接 auto ,那么现在就变得非常简单了,我们连类型都不关心了,只关心输入的这玩意是否满足可以被输出,这就是 conceptrequires 带来的能力。

那么我们看看 conceptrequires 是什么?

template <typename T>
concept OutputAble = requires(const T val) {
    std::cout << val;
};
auto main() -> int {
    std::cout << std::boolalpha << OutputAble<int> << '\n';
}

它输出了 true 。实际上,它有点像一个模板变量,当条件满足时为 true ,否则为 false ,那么条件在哪?

requires(const T val) { std::cout << val; }

这就是它所满足的条件,它需要一个 const T val 的变量,表示检查的操作一定不会修改到 val ,然后在 {} 中,它检查了 std::cout << val 是否是合法的,这显然比 std::enable_if 自然多了。const T val 只在编译期中存在,因此完全不用担心会影响到性能,只需要等到

实际上,我们刚刚一步一步简化的,就是 concept 的一些用法,我们可以使用 concept 先抽象出一些 requires 语句,然后复用它,或者直接在模板中使用 requires 来约束需要满足的 concept ,并且这个过程是可以再组合的,不用再写晦涩难懂的 std::enable_if 了。

标签:std,concept,20,cout,val,c++,const,requires,模板
From: https://www.cnblogs.com/FlandreScarlet/p/18067975

相关文章

  • PHPSTORM2019--【安装】【永久激活】【常用插件】
    一、【安装】安装前准备工作:1.关闭电脑网络,使其电脑无法联网状态2.hosts文件中添加0.0.0.0account.jetbrains.com0.0.0.0www.jetbrains.com 3.点击phpstorm2019.exe安装,我的安装目录:d:/software/phpstorm2019  二、【激活】1.jetbrains-agent.jar位于压缩包内,拷......
  • 杭电OJ 2054 A==B?
    A==B?题目只要你判断数字A是否等于B,傻子都知道肯定不是int数据类型可以存下的,A可能有1000位,要用字符串类型存储,比较的时候注意\(0001\)、\(001\)、\(1\)都是一样的,即前导零不算好吧,题目只说了number,没说是整数呀分析:比较前,先去掉前面多余的0,再去小数点后面多余的0(首先要保......
  • P8612 [蓝桥杯 2014 省 AB] 地宫取宝
    https://www.luogu.com.cn/problem/P8612#submit原始暴搜代码,没有记忆化,会tle#include<iostream>#include<vector>#include<algorithm>#include<math.h>#include<sstream>#include<string>#include<string.h>#include<iomanip&g......
  • 【2024面试刷题】二、Spring Cloud 面试题之Hystrix
    1、springcloud断路器的作用是什么?答:当一个服务调用另一个服务由于网络原因或自身原因出现问题时,调用者将等待被调用者的响应当更多的服务要求这些资源导致更多的请求等待时,就会出现连锁效应(雪崩效应)。断路器完全打开:一段时间内达到一定次数不能调用并且多次监测无恢复迹象......
  • 15_模板模式
    模板模式是一种行为型设计模式,它定义了一个抽象类作为算法的骨架,而将一些步骤的具体实现延迟到子类中。模板模式提供了一个统一的算法流程,但允许子类根据需要重写算法的具体步骤。模板模式有三个主要角色:抽象类(AbstractClass):定义了算法的骨架,包含了一个模板方法以及一些抽象......
  • 【题目】ccf csp 202309-3 梯队求解
    题目大意:给出需要求解的逆波兰表达式(后缀表达式),包含多个变量,现在每一次查询,给出所有变量的值,询问对于给定的变量其函数偏导值为多少。(仅包含乘、加减运算)(例如,对于表达式:x1x1x1*x2+*可转化为(x1*x1+x2)*x1对x1求偏导后变为(2*x1+x2)+(x1*x1+x2)带入x1=......
  • 洛谷题单指南-线性表-P2058 [NOIP2016 普及组] 海港
    原题链接:https://www.luogu.com.cn/problem/P2058题意解读:计算24小时时间窗口内不同国家的数量,是队列的典型应用。解题思路:本题需要用到两个关键的数据结构:队列、数组队列用来保存24小时内到达的船的时间,数组用来保存24小时内每个国家有多少人每到一只船,需要把时间放入队列,如......
  • 2024.03.12
         第5天所花时间(包括上课)2h代码量(行)140行博客量(篇)1篇学习到的知识点页面的跳转,和注册页面,登录页面的构建       packagecom.example.myapplication1;importandroidx.appcompat.app.AppCompatActivity;importandroid.an......
  • 洛谷题单指南-线性表-P1540 [NOIP2010 提高组] 机器翻译
    原题链接:https://www.luogu.com.cn/problem/P1540题意解读:本题模拟内存的调入调出,内存先入先出的特性就是队列。解题思路:本题需要两种数据结构:队列、数组队列用来模拟内存的操作,数组充当hash表用于判断单词在内存是否存在核心逻辑:对于每一个单词,如果内存不存在,查一次词典,再将......
  • 【2024-03-11】提好车了
    20:00手把青秧插满田,低头便见水中天。心地清净方为道,退步原来是向前。                                                 ——《插秧偈》唐·布袋和尚昨天去提了车,整个......