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 天,点击查看活动详情”