赞
踩
模板出现的目的便是解决同一个函数重复多遍而仅仅是为了不同类型的重载问题。例如,此函数将适用于任何数字类型。
- 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是一个只接受整数和浮点类型的概念的名称。现在,当用非数字参数编译这段代码时,就会得到编译错误。
可以用require关键字定义一个概念或约束:
- template<typename T>
- requires Numeric<T>
- T arg42(const T&arg){
- return arg+42;
- }
可以再模板声明中使用概念:
- template<Numeric T>
- T arg42(const T&arg){
- return arg+42;
- }
可以在函数签名处使用require关键字:
- template<typename T>
- T arg42(const T&arg)requires Numeric<T>{
- return arg+42;
- }
可以在参数列表中使用概念来简化函数模板:
- 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} { };
- };
可以在任何模板上使用概念和约束,简单起见我们将在这些示例中使用函数模板。
以下是一些注意点:
约束可以使用概念或类型特征来评估类型的特征。可以使用<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();
- }
require关键字是C++20中新出现的,为模板参数引入了一个约束。本例中,约束表达式根据类型特征is_integral测试模板参数。
可以使用<type_traits>头文件中预定义的特性,或者自定义的特性,就像模板变量一样。为了当作约束使用,该变量必须返回constexpr bool。例如:
- template<typename T>
- constexpr bool is_gt_byte{sizeof(T)>1};
这里定义了一个名为is_gt_byte的类型特征,该特性使用sizeof操作符来测试类型T是否大于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>都是原子表达式。以上虽然是这样描述的,但是在具体写代码的时候,可以将他们视为逻辑||.&&和!操作符
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。