Namespaces
Variants

requires expression (since C++20)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

生成一个类型为 bool 的纯右值表达式,用于描述约束条件。

目录

语法

requires { requirement-seq } (1)
requires ( parameter-list  (optional) ) { requirement-seq } (2)
parameter-list - 形参列表
requirement-seq - 由多个 要求 组成的序列,每个要求是以下类型之一:

说明

要求可以引用在作用域内的模板参数、 parameter-list 的参数,以及从外围上下文中可见的任何其他声明。

模板化实体 声明中使用的 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; // 正确:p 具有 T* 类型
};

若满足以下任一条件,则程序非良构:

  • 局部参数具有 默认实参
  • 参数列表以省略号结尾。
template<typename T>
concept C1 = requires(T t = 0)  // 错误:t 具有默认参数
{
    t;
};
template<typename T>
concept C2 = requires(T t, ...) // 错误:以省略号结尾
{
    t;
};

简单要求

表达式 ;
表达式 - 不以 requires 开头的表达式


一个简单的要求断言 expression 是有效的。 expression 是一个 unevaluated operand

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 标识符 ;
标识符 - (可能被限定的) 标识符 (包括 简单模板标识符


类型要求断言由 identifier 命名的类型是有效的:这可用于验证某个具名嵌套类型是否存在,或验证类/别名模板特化是否命名了一个类型。命名类模板特化的类型要求不要求该类型是完整的。

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)
expression - 表达式
type-constraint - 约束


复合要求断言 表达式 的属性。替换和语义约束检查按以下顺序进行:

1) 模板参数(如果存在)会被代入 expression 。
2) 若存在 noexcept 限定符, 表达式 不得为 潜在抛出 的。
3) 若存在 type-constraint ,则:
a) 模板参数被代入 type-constraint 。
b) decltype ( ( expression  ) ) 必须满足 type-constraint  施加的约束。否则,外围的 requires 表达式为 false

expression 是一个 unevaluated operand

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 约束表达式 ;
constraint-expression - 表示 约束条件 的表达式


嵌套需求可用于根据局部参数指定额外约束。 约束表达式 必须满足被替换的模板参数(如果存在)。将模板参数代入嵌套需求时,仅会按需对 约束表达式 进行替换以确定其是否得到满足。

template<class T>
concept Semiregular = DefaultConstructible<T> &&
    CopyConstructible<T> && CopyAssignable<T> && Destructible<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; }

关键词

requires

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 适用范围 发布时行为 正确行为
CWG 2560 C++20 未明确 requires 表达式中的参数类型是否被调整 同样需要调整
CWG 2911 C++20 出现在 requires
表达式中的所有表达式都是未求值操作数
仅部分
表达式是

参考文献

  • C++23 标准 (ISO/IEC 14882:2024):
  • 7.5.7 需求表达式 [expr.prim.req]
  • C++20 标准 (ISO/IEC 14882:2020):
  • 7.5.7 需求表达式 [expr.prim.req]

参见

Constraints and concepts (C++20) 指定模板参数的要求