requires expression
一种表达式,它很像一个lambda表达式,一个未命名元函数。例如:
requires(int a,int b){ a+b;}
其中:()部分是参数列表,{ }部分是需求列表。这个表达式是在编译期求值的,表达式的结果是true或者false。它的工作机制是:对于{ }内的若干条语句检查可行性,如果都能通过,则本条requires expression的值为true。在本例中,a+b;是提出了需求,即:有关两个int要求能相加。显然是满足的。则本例的表达式值为true。
int main(){
static_assert( requires(int a,int b){ a+b;} );
}
这个静态断言,证明了此种表示式是编译期求值的特性。
int main(){
static_assert( requires{ false; } ); //断言仍正确
}
上述语句,可以看出()部分是可选的,因此可省略。{ }部分中false;这个表达式是可行的,记住我们不关心被检查的表达式的结果,只关心它是否可行。{ }中是列举需求的,我们有四种需求描述方法:1)简单需求 2)类型需求 3)复杂需求 4)嵌套需求。
前文的a+b就是个简单需求。另外一个简单需求例子:
int main(){
static_assert( requires{ new int; } ); //OK
}
而当我们不但要检查表达式是否可行,还要检查表达式是否抛例外,需要用到复杂需求形式。可以像这样写:
int main(){
static_assert( requires{ (new int) noexcept; ); //ERROR
}
显然,new int是抛例外的,我们却需求不跑例外,需求不满足,requires表达式求值为false,则静态断言失败。 当我们需要表达式的值的类型符合某种要求时,这样写:
#include <type_traits>
int main(){
static_assert(
requires{ {new int} -> std::same_as<int*>; }
);
}
下一个是类型需求(Type Requirement)的例子
#include <vector>
int main(){
static_assert( requires{ typename std::vector<int>::iterator; } );
}
类型需求的标志就是typename,平铺直叙,直截了当。我们看到std::vector<int>::iterator这个内嵌类型是存在的。
下一个例子:
#include <type_traits>
#include <vector>
int main(){
static_assert(
requires(std::vector<int> v, int i){
v.at(i); //Simple Requirement
typename std::vector<int>::value_type; //Type Requirement
sizeof(int)==5; //? 居然检查通过了
}
);
}
看到sizeof(int)==5检查通过了,这是当然的,还记得Simple Requirements只检查表达式可行性? 如果要检查表达式的值,则需要使用Nest Requirements语法:
#include <type_traits>
#include <vector>
int main(){
static_assert(
requires(std::vector<int> v, int i){
v.at(i); //Simple Requirement
typename std::vector<int>::value_type; //Type Requirement
requires (sizeof(int)==4); //Nest Requirement
}
);
}
requires clause
C++对关键字充分榨取价值,往往一个关键字在多个场合使用。此处使用的是需求从句场合。用于限定模板函数的类型参数。
template<typename T>
requires true
T add(T a, T b) {
return a + b;
}
基本原理是,requires 之后一个常量表达式,当此表达式为true时,模板函数被选用。requires true恒成立,相当于对T没有限制。clause表达式可以使用的操作数为:1)基本表达式
template<typename T>
requires std::is_integral<T>::value //基本表达式
T add(T a, T b) {
return a + b;
}
什么是“基本表达式”(primary expression)? 文字量,标识符,fold表达式,还有requires表达式等,这些都是基本的。反例,2>1 就不是基本表达式,因为这个表达式可以分解为一个运算符> ,配合两个基本表达式1和2。所以:
template<typename T>
requires 2>1 //语法错误
T add(T a, T b) {
return a + b;
}
requires 2>1这个从句就是语法错误的。解决办法是加括号,括号内的是基本表达式。例如:
template<typename T>
requires (2>1) //括号括起来,作为一个整体的基本表达式
T add(T a, T b) {
return a + b;
}
#include <type_traits>
template<typename... Args>
requires (std::is_integral_v<Args> && ...) //折叠表达式 fold expression
int add(Args... a) {
return (a + ...);
}
int main(){
int a=1;
short b=2;
char c=3;
return add(a,b,c);
}
template<typename T>
requires requires(T a){ ++a; } //require表达式
T add(T a) {
return ++a;
}
int main(){
add(false); //自C++17开始,bool型无++运算,因此触发constraint报警
}
requires从句的constraint表达式部分,可以用逻辑运算符把基本表达式组合起来,例如:
#include <type_traits>
template<typename T>
requires std::is_integral_v<T> && ( sizeof(T)>=4 )
T add(T a) {
return ++a;
}
int main(){
short v=1; //因为short长度2字节,不符合>=4字节的这条限制,故触发报警
add(v);
}
constraint-expression
此表达式的基本表达式必须是返回bool纯右值(true或false),这些基本表达式用逻辑运算符连接。往往这个表达式是泛型的,当它生存在模板类或模板函数定义上下文中,就会出现模板的类型参数。
concept
终于到达了这个高级概念。前面说了constraint-expression用于表达对于类型应该张什么样子的需求描述。现在我们把这个需求集合整理起来,给起一个名字。这个机制就是C++20的concept机制。concept就是类型需求说明书。
#include <type_traits>
template<typename T>
concept My_First_Type_Requirement_Specification = //我的第一个类型需求规格说明书
std::is_integral_v<T> && //第一条:必选是整形
( sizeof(T)>=4 ) && //第二条:类型尺寸不小于4
requires(T a){ ++a; } //第三条:类型支持自++运算符。
;
template<typename T>
requires My_First_Type_Requirement_Specification<T> //requires从句
T add(T a) {
return ++a;
}
int main(){
short v=1; //因为short长度2字节,不符合>=4字节的这条限制,故触发报警
add(v);
}
concept可以看做可重用的元函数,我可以只在一处修改这个元函数,其它引用此元函数的地方都不必一一修改了。而requires表达式更像一个lambda表达式,用于ad-hoc用途(拉丁语:尽在此处使用),避免了先在很远的地方定义一个元函数,然后才能在使用的尴尬。
#include <type_traits>
#include <concepts>
//std::integral<T>是C++20 concept库中定义的一个东西,目的是不要大家重复造轮子。
//即一个简单的:template < class T > concept integral = std::is_integral_v<T>;
//requires-clause中使用concept。把requires true,替换为requires std::integral<T>
template<typename T>
requires std::integral<T> //constaint-expression, 要求std::integral<T>实例化为true
T add(T a) {
return ++a;
}
//把concept放到typename位置,要求concept是一元的谓词元函数,即C<T>形式
template<std::integral T>
T add(T a, T b) {
return a+b;
}
template<typename V,typename U>
concept binary_concept = true;
//ERROR: binary_concept是二元的谓词,不能这么写
template<binary_concept T> //binary_concept<T>,不符合binary_concept<V,U>
void add2(T a, T b) {
return a+b;
}
int main(){
static_assert(std::integral<int>);//static_assert(true), 把true替换走
//requires表达式中使用concept,就像表达式中调函数的意思。例如:(x + f(2))*10
static_assert( requires(){
std::integral<int> ; //调concept
} );
}
上面例子是concept一般可能出现的地方。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)