C++20概念(concept)和约束(constraint)探究

引入的原因

模板出现的目的便是解决同一个函数重复多遍而仅仅是为了不同类型的重载问题。例如,此函数将适用于任何数字类型。

template<typename T>
  T arg42(const T&arg){
    return arg+42;
  }

当我们用非数字类型去掉用它时,会发生什么呢?

const char*n="7";
cout<<"result is "<<arg42(n)<<endl;

输出为:

Result is ion

这样编译和运行并无错误,但是结果无法预测。这种调用很容易造成崩溃。因此我们希望这种调用能够提前由编译器生成一个错误信息。

有了概念后,便可以这样写:

template<typename T>
requires Numeric<T>
T arg42(const T& arg){
    return arg+42;
    }

require关键字是C++20的新特新,将该约束应用于模板。Numeric是一个只接受整数和浮点类型的概念的名称。现在,当用非数字参数编译这段代码时,就会得到编译错误。

使用概念或约束的方法

  1. 可以用require关键字定义一个概念或约束:

template<typename T>
requires Numeric<T>
T arg42(const T&arg){
     return arg+42;
     }
  1. 可以再模板声明中使用概念:

template<Numeric T>
T arg42(const T&arg){
   return arg+42;
   }
  1. 可以在函数签名处使用require关键字:

template<typename T>
T arg42(const T&arg)requires Numeric<T>{
    return arg+42;
    }
  1. 可以在参数列表中使用概念来简化函数模板:

auto arg42(Numeric auto& arg){
     return arg+42;
     }

看你的喜好来进行选择

一些简单的运用

概念只是一个命名的约束。上方的Numeric概念是这样的:

#include<concepts>
template<typename T>
concept Numeric =integral<T>||floating_point<T>;

此概念需要类型T,满足std::intergral或std::float_point预定义概念。这些概念包含在<concepts>头文件中。

概念和约束可以用在类模板,函数模板或变量模板中。我们已经看到了一个约束函数模板,这里是一个简单的约束类模板示例:

template<typename T>
requires Numeric<T>
struct Num{
   T n;
   Num(T n):n{n} { };
};

可以在任何模板上使用概念和约束,简单起见我们将在这些示例中使用函数模板。

以下是一些注意点:

  1. 约束可以使用概念或类型特征来评估类型的特征。可以使用<type_traits>头文件中找到任何类型特征,只要返回bool类型。

例如:

template<typename T>
requires is_integral<T>::value    //vaule是一个bool类型
double avg(vector<T> const& vec){
     double sum{accumulate(vec.begin(),vec.end(),0.0)};
     return sum/vec.size();
}
  1. require关键字是C++20中新出现的,为模板参数引入了一个约束。本例中,约束表达式根据类型特征is_integral测试模板参数。

  1. 可以使用<type_traits>头文件中预定义的特性,或者自定义的特性,就像模板变量一样。为了当作约束使用,该变量必须返回constexpr bool。例如:

template<typename T>
constexpr bool is_gt_byte{sizeof(T)>1};

这里定义了一个名为is_gt_byte的类型特征,该特性使用sizeof操作符来测试类型T是否大于1字节。

  1. 概念只是一组命名的约束

template<typename T>
concept Numeric =is_gt_byte<T>&&(integral<T>||floating_point<T>);

这定义了一个名为Numeric的概念,使用is_gt_byte约束,以及<concepts>头文件中的floating_point和integral概念。可以用它来约束模板,使其只接受大于1字节的数字类型。

还有一些内容

该标准使用术语连接,分离和原子来描述可用于构造约束的表达式类型。可以使用&&和||操作符组合概念和约束。这些组合分别称为连接词和析取词,可以把它们看成逻辑的AND和OR

约束连接符的例子:

template<typename T>
concept Integral_s =Integral<T>&&is_signed<T>::value;

约束析取符的例子:

template<typename T>
concept Numeric =integral<T> || floating_point<T>;

连接符和析取符都满足短路求值

原子约束使返回bool类型的表达式,不能进一步分解。换句话说,不是一个连接或析取。

template<typename T>
concept is_gt_byte =sizeof(T)>1;

也可以在原子约束中使用逻辑非!(NOT)操作符:

template<typename T>
concept is_byte =!is_gt_byte<T>;

当然,可以将所有这些表达式类型组合成一个更大的表达式。可以在下面的例子中看到这些约束表达式的例子:

template <typename T>
concept Numberic =is_gt_byte<T>&&(integral<T>||floating_point<T>);

子表达式(integral<T>||floating_point<T>)是一个析取。子表达式is_gt_byte<T>&&(...)是一个连接。每一个子表达式integral<T>,floating_point<T>和is_gt_byte<T>都是原子表达式。以上虽然是这样描述的,但是在具体写代码的时候,可以将他们视为逻辑||.&&和!操作符

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Reol520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值