C++ 20 Concept 语法

本文深入解析C++中的Requires表达式与Concepts机制,包括简单需求、类型需求、复杂需求及嵌套需求的使用,以及如何利用Concepts进行类型约束,提升代码的可读性和维护性。

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一般可能出现的地方。

内容概要:本文围绕可变桨叶四旋翼无人机的规范控制与点对点运动模拟展开,重点研究优化推力分配策略在翻转动作中的应用与性能比较。通过Matlab代码实现,构建了四旋翼动力学模型,并设计了多种控制算法以实现精确的姿态调整与轨迹跟踪。研究对比了不同推力分配方案在执行高机动性翻转动作时的稳定性、能耗效率与响应速度,旨在提升无人机在复杂飞行任务中的动态性能与控制精度。该仿真研究为无人机飞控系统的设计与优化提供了理论依据和技术支持。; 适合人群:具备一定自动控制理论基础和Matlab编程能力,从事无人机控制、飞行器动力学或机器人系统研究的科研人员及研究生。; 使用场景及目标:① 实现四旋翼无人机在三维空间中的精确点对点运动控制;② 对比分析不同推力分配策略在执行翻转等高难度动作时的控制效果与能耗表现,优化飞行性能;③ 为无人机自主飞行、特技飞行及复杂环境下的机动控制提供算法验证平台。; 阅读建议:此资源以Matlab仿真为核心,建议读者结合相关控制理论知识,深入理解代码实现细节,重点关注动力学建模、控制律设计与推力分配模块。在学习过程中,应动手调试参数,复现文中翻转动作的仿真结果,并尝试拓展至其他复杂飞行任务,以加深对无人机控制机理的理解。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值