红魔咖啡馆

头发越掉越多,头发越掉越少

0%

【C++】概念与约束

概念与约束

C++20引入了概念和约束的概念,用于限定模板类型实参的范围

新增了两个关键字concept与requires用于实现要求

requires

requires表达式

1
2
3
4
5
6
requires(Type1 para1, Type2 para2, ...){
    要求1;
    要求2;
    ...
    要求n;
}

require可以有0-n个参数,大括号内有0-n个表达式

require表达式是常量表达式,编译时求值,依次对要求进行判断,遇到第一个无效表达式时返回false,若所有表达式都是有效或良构的,返回true

注意:每条要求不会被实例化或求值,仅用于判断有效

例1:

1
2
3
4
5
6
7
8
9
10
11
12
template <typename T>
bool isAddable(T a, T b){
    bool result = 
        requires(T a, T b){
        a+b;
        a++;
        a+=b;
        ++a;
        {a*1}->std::convertible_to<T>;
    };
   	return result;
}

其中最后一条为复合要求

大括号里用于判断是否是有效的表达式,右箭头后面判断表达式的值是否符合某些条件

当T为int时,要求都可以成立,表达式的值为true

当T为string时,a++是无效的,表达式的值为false

例2:

1
2
3
4
5
6
7
8
template<typename T>
constexpr bool isIteratable(){
    bool result = 
        requires{
        	typename T::iterator;
    	};
    return result;
}

这个例子中,还有一种要求为条件要求

格式为typename 表达式,判断类型是否有效

当T为int时,int内没有迭代器,表达式的值为false

当T为string时,string内嵌了迭代器,表达式的值为true

嵌套要求

语法:require 约束表达式

1
2
3
4
5
6
7
8
template<typename T>
constexpr bool isFloat(){
    bool result = 
        requires{
        	requires std::is_floating_point_v<T>;
    };
    return result;
}

此处约束表达式用于判断是否是浮点数

requires子句

语法:requires 常量表达式(约束)

1
2
3
4
5
template<typename T>
requires std::is_integral<T>::value
bool is_even(T i){
    return i%2==0;
}

该函数模板设定了一个requires子句,判断传参是否为整型,只有满足该子句函数模板才能实例化,否则编译会报错

requires子句中的表达式也可以使用requires表达式

requires子句中也可以使用&&与||,前者需要满足所有约束,后者需要至少满足一项约束

concept

以上例子中的约束条件即为匿名概念,我们可以将约束条件定义为一个概念

1
2
template<模板参数列表>
concept ConcentName = 约束表达式;

定义后我们可以直接使用概念

1
2
3
4
5
template<typename T>
requires std::integral<T>
bool is_odd(T i){
    return i%2!=0;
}

这里requires子句中使用了预定义的概念integral,定义如下:

1
2
template<typename _Tp>
concept integral = is_integral_v<_Tp>;

或将模板中typename替换为概念

或使用概念+auto来定义形参

1
2
3
bool is_odd(std::integral auto i){
    return i%2!=0;
}

通常定义概念时,概念应该有明确语义

<concept>头文件中也提供了许多预定义概念,大部分类型特征都有对应概念定义