concept什么时候发布(C20尝鲜概念)
类模板,函数模板,以及非模板函数(常为类模板的成员),可以与约束(constraint)关联,它指定对模板实参的一些要求,这些要求可被用于选择最恰当的函数重载和模板特化。
这种要求的具名集合被称为概念(concept)。每个概念都是谓词,于编译时求值,并成为以之作为一项约束的模板接口的一部分。
具名要求(被命名的条件) --> 约束 --> 概念
概念是用来约束模板类型的条件集合。原来模板通过SIFNAE机制时,报错信息难以阅读,匹配的逻辑难写难读懂,并且这些约束条件和模板本身绑定在一起,不易复用。所以提出概念就是为了解决上述问题,它通过将模板的类型约束条件抽象出来,然后在模板定义时再使用它。这样成功解耦了模板类型约束条件和模板本身。
概念的目的是塑造语义分类(Number、Range、RegularFunction)而非语法上的限制(HasPlus、Array)。按照 ISO C 核心方针 T.20 所说,“与语法限制相反,指定有意义语义的能力是真正的概念的决定性特征。”
关系
概念通过约束表达式定义,使用概念的地方可以直接使用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 { 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; }
要求序列 中的每个要求是下列之一:
- 简单要求(simple requirement)
template<typename T>
concept Addable =
requires (T a, T b) {
a b; // 该表达式是不求值操作数;只检查语言正确性。
};
- 类型要求(type requirement)
template<typename T> using Ref = T&;
template<typename T> concept C =
requires {
typename T::inner; // 要求的嵌套成员名
typename S<T>; // 要求的类模板特化
typename Ref<T>; // 要求的别名模板替换
};
- 复合要求(compound requirement)
{ 表达式 } 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
};
- 嵌套要求(nested requirement)
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] }; // 复合
};
概念 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;
}
在线编译测试
https://wandbox.org/nojs/gcc-head
https://wandbox.org/nojs/clang-head
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com