1 概述
C++20引入了一个重要的特性-概念。也引入了两个关键字 concept
和 requires
,分别对应概念和约束。
优点
- concept提供了一种在模板编程中,对类型进行约束和限制的方法,简洁明确。
- requires提供了一种简单的约束编写方式。
下面,先讲约束,再将概念。
2 约束
约束以两种形式表现,分别为约束表达式和约束子句。
1 requires约束子句
语法:requires cond1 && cond2 && ...
以 requires 开头,后面跟上约束子句。约束子句是一个条件表达式,由一系列约束条件组成,可以使用 &&
和 ||
。
比如:
requires true && sizeof(int) == 4 && std::same_as<int, int>;
2 约束表达式
语法:requires(...) {...}
以requires开头,圆括号里面是参数列表,可以声明在表达式中使用的变量,大括号里面是一系列约束要求。
分类
大括号内的约束要求主要分为三种:
- 普通的约束要求,要求可执行。
- 约束子句
- {} -> 这样的特殊约束方式
下面分别举例说明。
比如
- 普通约束
template <typename T>
concept cpt = requires(T t, int a) {
//不要多想,typename只不过是告诉编译器value_type是一个类型名而不是一个变量名。
typename T::value_type; //约束T类中有一个value_type类型别名
T::value;//约束T类中有一个value静态变量或常量
t.print();//约束T类的实例应该可以调用这个方法
};
- 约束子句
template <typename T>
concept cpt = requires (T t) {
requires sizeof(T) == 4 && std::same_as<T, int> || true;
//这里应该能看处理,约束子句和约表达式可以来回嵌套使用
requires requires(int) {
requires sizeof(int) == 4;
};
requires sizeof(int);//error:不是bool
requires T::val; //如果val不是bool类型,也是错误的
requires static_cast<bool>(T::val);//对的
};
- 特殊约束
首先说明一下,每个部分的作用。
{}
推导类型,可以推导变量的类型,也可以推导函数调用返回值类型。
->
用来传递使用{}
推导的类型给"概念"。(应该是只能传递{}
推导的类型)
接下来,再来说明一下推导的规则。
- 左值
->
类型引用 - 纯右值
->
普通类型 - 将亡值
->
右值引用 - 函数调用
->
函数返回值类型 - 修饰符在推导时不会丢弃
template <typename T>
concept test1 = requires (T t, T&& t_) {
{t} -> std::same_as<T&>; //T类型的左值
{T{}} -> std::same_as<T>; //一个匿名右值
{t_} -> std::same_as<T&>; //T&&类型的左值
{std::move(t)} -> std::same_as<T&&>; //一个将亡值
{t.print()} noexcept; //不会抛出异常
{ std::declval<int>()} noexcept -> std::same_as<int&&>; // 该函数不会抛出异常,并且返回值类型为int&&
};
总结
- 约束子句和约束表达式可以任意嵌套使用
- 可以使用约束子句的地方,不可以使用约束表达式,反之,同理
- 约束表达式是一个常量bool表达式, 约束子句每一个约束条件也必须是bool类型
- 约束表达式可以在任何地方使用,比如
if语句
,但requires
约束子句在约束表达式中或模板声明中使用。
3 概念
1 概念的定义
下面是一个最简单的概念模板
template <typename T>
concept cpt = true;
引出定义概念的语法:
模板变量的声明
concept cpt = 约束子句;
其中,约束子句的使用和上面讲的相同。
举例
定义了一个概念,约束模板参数要是一个无符号的整数类型。
template <typename T>
concept cpt = std::is_integral_v<T> && std::is_unsigned<T>::value;
2 使用
1 模板声明处
- 替代typename 声明一个模板参数
template <cpt T>
void fun ();
- 约束auto推导的类型
cpt auto a = 1;
2 作为常量布尔表达式使用
if constexpr (cpt<int>) {}
if constexpr (requires(int a) {typename type::value_type;}) {}
看得再多不如自己写一遍。我也是看他人的,自己多尝试,又重新做了总结。
如果有什么错误,可以指出。