介绍

类模板,函数模板,以及非模板函数(常为类模板的成员),可以与约束(constraint)关联,它指定对模板实参的一些要求,这些要求可被用于选择最恰当的函数重载模板特化

这种要求的具名集合被称为概念(concept)。每个概念都是谓词,于编译时求值,并成为以之作为一项约束的模板接口的一部分。

具名要求(被命名的条件) --> 约束 --> 概念

概念是用来约束模板类型的条件集合。原来模板通过SIFNAE机制时,报错信息难以阅读,匹配的逻辑难写难读懂,并且这些约束条件和模板本身绑定在一起,不易复用。所以提出概念就是为了解决上述问题,它通过将模板的类型约束条件抽象出来,然后在模板定义时再使用它。这样成功解耦了模板类型约束条件和模板本身。

概念的目的是塑造语义分类(Number、Range、RegularFunction)而非语法上的限制(HasPlus、Array)。按照 ISO C 核心方针 T.20 所说,“与语法限制相反,指定有意义语义的能力是真正的概念的决定性特征。”

concept什么时候发布(C20尝鲜概念)(1)

关系

概念通过约束表达式定义,使用概念的地方可以直接使用requires约束表达式。

约束、概念和 requires表达式都是编译器常量表达式 bool 值,并且可以作为普通值使用,例如在 if constexpr 中。

概念定义的形式:

template < 模板形参列表 >

concept 概念名 = 约束表达式;

template < class T > concept integral = std::is_integral_v<T>;

约束

约束是逻辑操作和操作数的序列,它指定对于模板实参的要求。它们可在 requires 表达式中出现,也可直接作为概念的主体

有三种类型的约束:

1) 合取 &&(conjunction)

2) 析取 ||(disjunction)

3) 原子约束(atomic constraint)

原子约束应当为 bool 类型的纯右值常量表达式,当且仅当它求值为 true 时该约束得以满足。

template <class T> concept Integral = std::is_integral<T>::value; template <class T> concept SignedIntegral = Integral<T> && std::is_signed<T>::value; template <class T> concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;

template <class T = void> requires EqualityComparable<T> || Same<T, void> struct equal_to;

requires表达式:

requires 表达式的语法如下:

requires { requirement-seq }

requires ( 形参列表(可选) ) { 要求序列 }

它是 bool 类型的纯右值表达式,描述对一些模板实参的约束。若约束得到满足则这种表达式为 true,否则为 false:

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; } // 第一个是requires 子句,第二个是requires 表达式 T add(T a, T b) { return a b; }

要求序列 中的每个要求是下列之一:

template<typename T> concept Addable = requires (T a, T b) { a b; // 该表达式是不求值操作数;只检查语言正确性。 };

template<typename T> using Ref = T&; template<typename T> concept C = requires { typename T::inner; // 要求的嵌套成员名 typename S<T>; // 要求的类模板特化 typename Ref<T>; // 要求的别名模板替换 };

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

template<typename T> concept C2 = requires(T x) { {*x} -> std::convertible_to<typename T::inner>; // 表达式 *x 必须合法 // 并且 类型 T::inner 必须合法 // 并且 *x 的结果必须可以转换为 T::inner {x 1} -> std::same_as<int>; // 表达式 x 1 必须合法 // 并且 std::Same<decltype((x 1)), int> 必须被满足 // 亦即,(x 1) 必须为 int 类型的纯右值 {x * 1} -> std::convertible_to<T>; // 表达式 x * 1 必须合法 // 并且其结果必须可以转换为 T };

template <class T> concept Semiregular = DefaultConstructible<T> && CopyConstructible<T> && Destructible<T> && CopyAssignable<T> && requires(T a, 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子句):

概念 auto

template < 模板形参列表 >

requires 概念

T func(T a)

template < 模板形参列表 >

T func(T a) requires 概念

template < 概念 T > 去除typename关键字

T func(T a)

例子1:

#include <iostream> #include <concepts> std::integral auto add1(std::integral auto a, std::integral auto b) { return a b; } template<typename T> requires std::integral<T> T add2(T a, T b) { return a b; } template<typename T> T add3(T a, T b) requires std::integral<T> { return a b; } template<std::integral T> T add4(T a, T b) { return a b; } int main(int argc, char *argv[]) { std::cout << add1(1, 2) << std::endl; std::cout << add2(1, 2) << std::endl; std::cout << add3(1, 2) << std::endl; std::cout << add4(1, 2) << std::endl; return 0; }

例子2:

#include <iostream> #include <concepts> #include <stddef.h> template<class T> concept A = requires (T t) { t.has(); } && !requires (T t) { t.hasnt(); }; template<class T> concept B = requires (T t) { requires requires { t.has(); }; requires !requires { t.hasnt(); }; }; template<class T> concept C = requires (T t) { t.has(); !requires { t.hasnt(); }; }; struct S1 {}; struct S2 { void has(); }; struct S3 { void hasnt(); }; struct S4 { void has(); void hasnt(); }; int main(int argc, char *argv[]) { static_assert(!A<S1>); static_assert(A<S2>); static_assert(!A<S3>); static_assert(!A<S4>); static_assert(!B<S1>); static_assert(B<S2>); static_assert(!B<S3>); static_assert(!B<S4>); static_assert(!C<S1>); static_assert(C<S2>); static_assert(!C<S3>); static_assert(C<S4>); return 0; }

concept什么时候发布(C20尝鲜概念)(2)

在线编译测试

https://wandbox.org/nojs/gcc-head https://wandbox.org/nojs/clang-head

,