Concepts
模版存在一些固有的问题:我们并没有办法对模版参数引入相应的限制
- 参数是否可以正常工作,通常需要阅读代码进行理解
- 编译报错的友好性较差(vector<int &>)
为了解决这个问题,在C++20引入了Concepts
:编译器谓词,他可以基于给定的输入,返回true
或false
,他可以与constraints
(require
从句)一起使用来限制模版参数。通常我们将constraints
置于表示模版形参的尖括号后面进行限制
template <typename T>
concept IsAvail = std::is_same_v<T,int> || std::is_same_v<float,T>;
template <typename T>
requires IsAvail<T>
void fun(T input){
}
关于Concept
的定义与使用,我们需要注意以下几点
- 包含一个模版参数的
Concept
- 使用
require
从句 - 直接替换
typename
template <typename T> concept IsAvail = std::is_same_v<T,int> || std::is_same_v<float,T>; template <IsAvail T> void fun(T input){ }
- 使用
- 包含多个模版参数的
Concept
当用作类型template <typename T, typename T2> concept IsAvail = std::is_same_v<T,T2> ; template <typename T1, typename T2> requires IsAvail<T1,T2> void fun(T1 input1, T2 input2){ }
constraint
时,可以少传递一个参数,推导出的类型将作为首个参数template <typename T, typename T2> concept IsAvail = std::is_same_v<T,T2> ; template <IsAvail<int> T> // 等价于 IsAvail<T,int> void fun(T input){ }
之后我们来关注require
表达式,它主要分为以下几类
- 简单表达式:表明可以接收的类型
template <typename T> concept Addable = requires (T a, T b){ a+b; };
- 类型表达式:表明是一个有效的类型
template <typename T> concept Avail = requires { typename T::inter; }
- 符合表达式:表明操作的有效性,以及操作返回类型的特性
template <typename T> concept Avail = requires (T x) { {x+1} -> int ; }
- 嵌套表达式:包含其它的限定表达式
需要注意区分
requires
从句与requires
表达式,具体参考这里
requirea
从句会影响重载解析与特化版本的选取
- 只有 requires 从句有效而且返回为 true 时相应的模板才会被考虑
- requires 从句所引入的限定具有偏序特性,系统会选择限制最严格的版本
template <typename T> concept C1 = std::is_same_v<T,int>; template <typename T> concept C2 = std::is_same_v<T,int> || std::is_same_v<T,float>; template <C1 T> void fun(T){ std::cout << "1\n"; } template <C2 T> void fun(T){ std::cout << "2\n"; } int main() { fun(3); //输出为1 }
这里有一个特化小技巧:我们在声明中引入“ A||B” 进行限制,之后分别针对 A 与 B 引入特化
template <typename T>
requires std::is_same_v<T,int> || std::is_same_v<T,double>
class B;
template <>
class B<int>{
};
template <>
class B<double>{
};