requires 表达式 (C++20 起)
产生描述约束的 bool 类型的纯右值表达式。
语法
requires { 要求序列 }
|
(1) | ||||||||
requires ( 形参列表 (可选) ) { 要求序列 }
|
(2) | ||||||||
| 形参列表 | - | 形参列表 |
| 要求序列 | - | 要求 序列,每个要求都属于以下之一: |
解释
要求可以指代处于作用域内的模板形参,形参列表 中的形参,以及在上下文中可见的任何其他声明。
将模板参数替换到模板化实体的声明中所使用的 requires 表达式中,可能会导致在其要求中形成无效的类型或表达式,或者违反这些要求的语义。在这种情况下,requires 表达式的值为 false 而不会导致程序非良构。按照词法顺序进行替换和语义约束检查,当遇到决定 requires 表达式结果的条件时就停止。如果替换(若存在)和语义约束检查成功,那么 requires 表达式的结果是 true。
如果某个 requires 表达式在以任何模板实参替换后都会导致替换失败,那么程序非良构,不要求诊断:
template<class T>
concept C = requires
{
new int[-(int)sizeof(T)]; // 对任何 T 都无效: 非良构,不要求诊断
};
如果 requires 表达式在其要求中包含无效的类型或表达式,并且它没有出现在模板化实体的声明中,则程序非良构。
局部形参
requires 表达式可以使用形参列表引入局部形参。这些形参没有链接、存储和生存期;它们只是为了定义要求而用作标记。
每个形参的类型通过与确定函数形参的实际类型相同的方式确定:
template<typename T>
concept C = requires(T p[2])
{
(decltype(p))nullptr; // OK,p 的类型为 T*
};
如果满足以下任意条件,那么程序非良构:
- 某个局部形参有默认实参。
- 形参列表以省略号结尾。
template<typename T>
concept C1 = requires(T t = 0) // 错误:t 有默认实参
{
t;
};
template<typename T>
concept C2 = requires(T t, ...) // 错误:以省略号结尾
{
t;
};
要求
简单要求
表达式 ;
|
|||||||||
| 表达式 | - | 不以 requires 开始的表达式
|
简单要求断言表达式 是有效的。表达式 是不求值操作数。
template<typename T>
concept Addable = requires (T a, T b)
{
a + b; // "需要表达式 “a + b” 是可以通过编译的有效表达式"
};
template<class T, class U = T>
concept Swappable = requires(T&& t, U&& u)
{
swap(std::forward<T>(t), std::forward<U>(u));
swap(std::forward<U>(u), std::forward<T>(t));
};
以关键词 requires 开始的要求总是被解释为嵌套要求。因此简单要求不能以没有括号的 requires 表达式开始。
类型要求
typename 标识符 ;
|
|||||||||
| 标识符 | - | (可有限定的)标识符(包括简单模板标识) |
类型要求断言标识符 指名的类型是有效的:可以用来验证指名的嵌套类型是否存在,或者某个类/别名模板特化是否指名了某个类型。指名类模板特化的类型要求并不要求该类型是完整的。
template<typename T>
using Ref = T&;
template<typename T>
concept C = requires
{
typename T::inner; // 需要嵌套成员名
typename S<T>; // 需要类模板特化
typename Ref<T>; // 需要别名模板替换
};
template<class T, class U>
using CommonType = std::common_type_t<T, U>;
template<class T, class U>
concept Common = requires (T&& t, U&& u)
{
typename CommonType<T, U>; // CommonType<T, U> 是合法的类型名
{ CommonType<T, U>{std::forward<T>(t)} };
{ CommonType<T, U>{std::forward<U>(u)} };
};
复合要求
{ 表达式 };
|
(1) | ||||||||
{ 表达式 } noexcept ;
|
(2) | ||||||||
{ 表达式 } -> 类型约束 ;
|
(3) | ||||||||
{ 表达式 } noexcept -> 类型约束 ;
|
(4) | ||||||||
| 表达式 | - | 表达式 |
| 类型约束 | - | 约束 |
复合要求断言表达式 的属性。替换和语义约束检查按以下顺序进行:
decltype((表达式)) 必须满足类型约束 蕴含的约束。否则,被包含的 requires 表达式是 false。表达式 是不求值操作数。
template<typename T>
concept C2 = requires(T x)
{
// 表达式 *x 必须合法
// 并且 类型 T::inner 必须存在
// 并且 *x 的结果必须可以转换为 T::inner
{*x} -> std::convertible_to<typename T::inner>;
// 表达式 x + 1 必须合法
// 并且 std::same_as<decltype((x + 1)), int> 必须满足
// 即, (x + 1) 必须为 int 类型的纯右值
{x + 1} -> std::same_as<int>;
// 表达式 x * 1 必须合法
// 并且 它的结果必须可以转换为 T
{x * 1} -> std::convertible_to<T>;
};
嵌套要求
requires 约束表达式 ;
|
|||||||||
| 约束表达式 | - | 用来表示约束的表达式 |
嵌套要求可用于根据本地形参指定其他约束。约束表达式 必须由被替换的模板实参(如果存在)满足。将模板实参替换到嵌套要求中会导致替换到约束表达式 中,但仅限于确定是否满足约束表达式 所需的程度。
template<class T>
concept Semiregular = DefaultConstructible<T> &&
CopyConstructible<T> && Destructible<T> && CopyAssignable<T> &&
requires(T a, std::size_t n)
{
requires Same<T*, decltype(&a)>; // 嵌套:"Same<...> 求值为 true"
{ a.~T() } noexcept; // 复合:"a.~T()" 是不会抛出的合法表达式
requires Same<T*, decltype(new T)>; // 嵌套:"Same<...> 求值为 true"
requires Same<T*, decltype(new T[n])>; // 嵌套
{ delete new T }; // 复合
{ delete new T[n] }; // 复合
};
注解
关键词 requires 也用来引入 requires 子句。
template<typename T>
concept Addable = requires (T x) { x + x; }; // requires 表达式
template<typename T> requires Addable<T> // requires 子句,不是 requires 表达式
T add(T a, T b) { return a + b; }
template<typename T>
requires requires (T x) { x + x; } // 临时的约束,注意关键词用了两次
T add(T a, T b) { return a + b; }
关键词
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
| 缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
|---|---|---|---|
| CWG 2560 | C++20 | 不明确 requires 表达式中是否会调整形参类型
|
也会调整 |
| CWG 2911 | C++20 | requires 表达式中出现的所有表达式都是不求值操作数
|
只有部分表达式是 |
引用
- C++23 标准(ISO/IEC 14882:2024):
- 7.5.7 Requires expressions [expr.prim.req]
- C++20 标准(ISO/IEC 14882:2020):
- 7.5.7 Requires expressions [expr.prim.req]
参阅
| 约束与概念 (C++20) | 规定对模板实参的要求 |