c++ 需求表达式

390 阅读3分钟

c++ 需求表达式

Require 表达式(不同于 require 子句)提供简单但灵活的语法来指定对一个或多个模板参数的多个要求。您可以指定:

  • 必需的类型定义
  • 必须有效的表达式
  • 对表达式生成的类型的要求

require表达式以require开头,后面跟着一个可选参数列表,然后是一个需求块(都以分号结尾)。例如

template<typename Coll> 

...

requires{

typename Coll::value_type::first_type;   //元素/值具有 first _ type

 typename Coll::value_type::second_type;  //元素 具有 second_type


 }

可选参数列表允许您引入一组“虚拟变量”,可用于在 require 表达式的主体中表达需求:

template<typename T> 

 requires(T x,Ty){

x+y;

 x-y;

 }

这些形参永远不会被实参替换。因此,通过值还是引用来声明它们通常并不重要。参数还允许我们引入子类型的(参数)

template<typename Coll>

...requires(typename Coll::value_type v) {

 std::cout <<v;

 }

该要求检查Co11::value类型是否有效,以及该类型的对象是否支持输出操作符。

当使用此方法仅检查Co11::值类型是否有效时,您不需要在需求块的主体中添加任何内容。

但是,该块不能为空。所以,你可以简单地使用true

template<typename Coll>

... requires(typename Coll::value_type v) {

 true;

 }

简单的要求

简单的需求只是必须格式良好的表达式。这意味着调用必须进行编译。调用不会被执行,因此操作是否定义了行为或是否产生true并不重要。例如

template<typename T1, typename T2>

...requires(T1 val, T2 p) {

	*p;

	p[0];

	p->value();

	*p> val;

	p==nullptr;

	}

最后一个调用不要求 p 是 nullptr(要要求这样做,您必须检查 T2 是否为 std::nullptr_t 类型)。相反,我们要求我们可以将 T2 类型的对象与 std::nullptr_t(nullptr 的类型)的对象进行比较。

使用操作符 || 通常没有意义。

一个简单的需求,例如

*p> val II p== nullptr;

不要求左子表达式或右子表达式都是可能的。

它提出了可以将两个子表达式的结果与operatorll结合的要求。

要求两个子表达式中的任何一个,必须使用

template<typename T1, typename T2> 

... requires(T1 val, T2 p) {

*p> val;

}

Il requires(T2 p) { 

p==nullptr;

}

还要注意,这个概念并不要求T是整型

template<typename T>

...requires {

std::integral<T>;

};

它只要求表达式std::integral有效,这是所有类型的情况。相反,你必须把它写成这样

    template<typename T>
 ... std::integral<T> &&

requires {
...

}; 

或如下:

    template<typename 

...requires {

requires std::integral<T>;

...

};

类型约束

类型需求是在使用类型名称时必须格式良好的表达式。这意味着必须将指定的名称定义为有效类型。

    template<typename T1, typename T2> 

...requires {

	typename T1::value_type;

	typename std::ranges::iterator_t<T1>; 

	typename std::ranges::iterator_t<std::vector<T1>>; 

	typename std::common_type_t<T1,T2>;

	 }

对于所有类型需求,如果类型存在但为空,则满足需求。

注意,您只能检查给类型的名称(类名、枚举类型、来自typedef或using)。不能使用该类型检查其他类型声明

    template<typename T> 

	...requires{

	typename int;

 	typename T&;

 }

测试后者的方法是声明一个相应的参数

         template<typename T>

	...requires(T&){ .

	 true;

};

同样,需求检查使用传递的类型定义另一个类型是否有效。

    template<std::integral T> 

class MyType1 {

...

};

template<typename T>

 requires requires { 

typename MyType1<T>;

}

void mytype1(T){ 
..
}

mytype1(42);
mytype1(7.7);

因此,下面的要求不会检查类型T是否存在标准哈希函数

     template<typename T>

concept StdHash = requires {

typename std::hash<T>; 
};

这样做的方法是尝试创建或使用它:

    template<typename T>

concept StdHash =requires {

std::hash<T>{}; 

 };

使用始终生成类型的类型函数是没有意义的:

    
    template<typename T>

... requires{ 

 typename std::remove_const_t<T>;

 }

该要求只检查类型表达式是否产生类型,在这里总是这样。要检查constness,使用

    template<typename T>

... std::is_const<T> 

使用可能具有未定义行为的类型函数也没有意义。例如,类型trait std::make unsigned<>要求传递的参数是整型而不是bool类型。如果不是这样,就会得到未定义的行为。如果你把它作为一个要求

std::make_unsigned_r<T>::type

只能符合要求或产生未定义行为(这可能意味着仍然符合要求)。


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情