1、再谈约束主句与从句
上一篇文章中提到过约束可以无限嵌套。末尾也提到不考虑嵌套约束的情况下,模板因为 SFINAE 规则的存在,其中 requires 子句只要存在返回值,只有可能是 true 这一种结果。在非模板中,如果 requires 子句中的表达式非法是直接报错。
那如果 requires 子句中的表达式是合法的但是不符合要求呢?如下图所示。接下来再讨论嵌套约束的情况。
不同于布尔值的 true false ,requires 主句对后续表达式的处理后将得到符合要求与不符合要求两种结果。非法表达式也是一种不符合要求的情况。在 SFINAE 规则中,这两种不符合要求是等价的。简单概括就是,主句将布尔值转换为是否符合要求,从句将是否符合要求转换为布尔值。因此如第一张图中所示, requires 嵌套可以通过来回转换进而层层向外传递,最终被 r 接收。
以上这段内容对理解约束至关重要。
2、复合约束
1、decltype
在讨论复合约束之前,需稍微补充个 decltype 的冷知识。先看现象:
之所以唯独 decltype((n)) 不是 int 类型,因为规定了当 decltype 中的表达式是括号表达式并且括号中表达式是左值时,将它当作左值引用类型。
2、约束中的“{}”和“->”
复合约束更像是 requires 嵌套的简便写法。其实并没简便多少,但是更符合语义。
上图中①和②是等价的。其中①是简便写法,编译器会自动将 {表达式} 识别为 decltype((表达式)),解析后插入到 “->” 符号后紧跟的概念的第一个类型参数中。 “->” 符号之后只允许使用概念。而②的表达式允许使用概念或者布尔类型的常量模板。
刚才提到的 decltype 补充要点在这里需要十分注意,避免写出以下代码:
此外,花括号后还可用 noexcept 来指明花括号中的表达式不会抛出错误。
3、模板偏特化中的约束
现在要对某几个类型的模板类型形参进行匹配到偏特化版本。模板匹配规则是找最匹配的模板进行实例化。最匹配的自然是符合的要求越多越匹配。因此可以对模板进行约束以增加匹配要求来进行偏特化。
同样,也可以使用概念来偏特化该模板。在这里可以感觉到,概念更像是约束的容器,虽然它的表达式是布尔值。
自然地,也可以对特定类型的指针、引用、CV限定符进行特化:
4、总结
概念与约束的全部内容到此就以全部介绍完毕。要彻底了解概念与约束,只需要当作拼积木一般,将所有要求进行分类,如是否合法,是否符合要求,重点在于知道自己在写什么。尤其是约束,用法其实并不多,但初次接触难免觉得杂乱,需要多加使用才能掌握。