requires 表达式 (C++20 起)

来自cppreference.com
< cpp‎ | language


产生描述约束的 bool 类型的纯右值表达式。

语法

requires { 要求序列 }
requires ( 参数列表(可选) ) { 要求序列 }
参数列表 - 用逗号分隔的参数列表,就像在函数声明中一样,但不允许使用默认参数,而且不能以省略号结尾(不能是变参函数)(允许模板形参包)。这些参数没有存储周期、链接性质或生命周期,只用于协助指定要求。这些参数的作用域直到 要求序列 的结束符 }
要求序列 - 要求序列,每个要求都是如下形式之一:
  • 简单要求
  • 类型要求
  • 复合要求
  • 嵌套要求

解释

要求可以引用作用域内的模板参数、参数列表中引入的局部参数,以及其他任何在上下文中可见的声明。

将模板参数替换到模板化实体的声明中使用的 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 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)} }; 
};

复合要求

复合要求具有如下形式

{ 表达式 } noexcept(可选) 返回类型要求(可选) ;
返回类型要求 - -> 类型约束

并断言命名表达式的属性。替换和语义约束检查按以下顺序进行:

1) 模板参数 (若存在) 被替换到表达式
2) 如果使用 noexcept表达式 一定不能潜在抛出
3) 如果 返回类型要求 存在,则:
a) 模板参数被替换到 返回类型要求
b) decltype((expression)) 必须满足 类型约束 的约束。否则,被包含的 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<...> 被求值为真"
    { a.~T() } noexcept; // 复合:"a.~T()" 是不会抛出的合法表达式
    requires Same<T*, decltype(new T)>; // 嵌套:"Same<...> 被求值为真"
    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++20) 规定模板参数的要求