类模板,函数模板,以及非模板函数(通常是类模板的成员),可以与一项约束 相关联,它指定了对模板实参的一些要求,这些要求可以被用于选择最恰当的函数重载和模板特化。
这种要求的具名集合被称为概念。每个概念都是一个谓词,它在编译时求值,并在将之用作约束时成为模板接口的一部分。
concept语法
概念是要求的具名集合。概念的定义必须在命名空间作用域中出现。概念定义拥有以下形式:
template < 模板形参列表 > concept 概念名 属性 (可选) = 约束表达式; 属性 - 任意数量的属性的序列 |
概念不能被显式实例化、显式特化或部分特化(不能更改约束的原初定义的含义)。
概念可以在标识表达式中命名。该标识表达式的值在满足约束表达式时是 true,否则是 false。
概念在作为以下内容的一部分时也可以在类型约束中被命名:
模板类型形参声明
占位类型说明符
复合要求
requires语法
requires { 要求序列 } (1) requires ( 形参列表 (可选) ) { 要求序列 } (2) |
requires可以放在模版中,也可以放在函数之后,但是不可以放在类之后。
concept约束表达式
template < 模板形参列表 > concept 概念名 属性 (可选) = 约束表达式; |
常量表达式
template <typename T>
concept ConExp = sizeof(T) == 4;
requires表达式
requires { requirements; }
requires (parameter-list) { requirements; }
简单要求
简单要求是任何不以关键词 requires 开始的表达式语句。它断言该表达式是有效的。表达式是不求值的操作数;只检查语言的正确性。
template<typename T>
concept Cadd = requires (T a, T b)
{
a + b; // "需要表达式 “a + b” 是可以通过编译的有效表达式"
};
类型要求
类型要求是关键词 typename 后面接一个可以被限定的类型名称。该要求是,所指名的类型是有效的:可以用来验证某个指名的嵌套类型是否存在,或者某个类模板特化是否指名了某个类型,或者某个别名模板特化是否指名了某个类型。指名类模板特化的类型要求并不要求该类型是完整的。
template <typename T>
concept C = requires {
typename T::get(); //T具备get成员函数
};
template<typename T>
concept C = requires
{
typename T::inner; // 需要嵌套成员名
typename S<T>; // 需要类模板特化
typename Ref<T>; // 需要别名模板替换
};
template<typename T>
concept cOpt = requires(T a, T b)
{
// 要求a + b的结果可以转换为T类型
{ a + b } -> std::convertible_to<T>;
};
复合要求
{ 表达式 } noexcept(可选) 返回类型要求 (可选) ;
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>;
};
嵌套要求
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] }; // 复合
};
组合concept约束表达式
现有的 Concept 表达式可以通过使用逻辑运算符“与”(&&)和“或”(||)来组合。
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;
模板中concept的使用
作为模板参数约束,替换typename
template <ConExp T>
T add(T a, T b)
{
return a + b;
}
使用requies约束
template <typename T>
requires ConExp<T> // 只能放在类前面,函数模板可以放在函数名后面
class AA
{
public:
static T add(T a, T b)
{
return a + b;
}
};
内联requies约束
template <typename T>
T add3(T a, T b)requires requires(T) { sizeof(T) == 4; }
{
return a + b;
}
使用标准库提供的 concept约束
核心语言 Concepts:same_as、derived_from、convertible_to、integral、floating_point、copy_constructible 等。
比较 Concepts:equality_comparable、totally_ordered 等。
对象 Concepts:movable、copyable 等。
可调用 Concepts:invocable、predicate 等。
#include <concepts> //c++20提供std::integral、convertible_to、same_as~~
template <typename T>
requires std::integral<T>
T add4(T a, T b)
{
return a + b;
}
结合auto函数重载
std::floating_point auto add4(std::floating_point auto a, std::floating_point auto b)
{
return a + b;
}
部分参数约束
template <unsigned int i>
requires (i <= 20)
int add6(int j)
{
return i + j;
}
//调用:add6<20>(100)