requires 表达式 (C++20 起)

来自cppreference.com
< cpp‎ | language


 
 
 
 
 
 

产生描述约束的 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)
表达式 - 表达式
类型约束 - 约束


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

1) 模板实参(如果存在)被替换到表达式 中。
2) 如果使用了 noexcept,那么表达式 一定不能潜在抛出
3) 如果类型约束 存在,那么:
a) 模板实参被替换到类型约束 中。
b)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;}

[编辑]关键词

requires

[编辑]缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 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) 规定对模板实参的要求[编辑]
close