C++20 concepts
使用 concepts 的4种方式
template < class T >
concept floating_point = std::is_floating_point_v<T>;
一般 requires 从句
将 requires 从句放在 模板参数列表 与 函数返回值 之间。
template <typename T>
requires floating_point<T> // requires 从句的表示形式1 = requires + 约束表达式(concept / type trait)
auto add(T a, T b) {
return a + b;
}
或
template <typename T>
requires requires() { // requires 从句的表示形式2 = requires + requires 表达式
requires (!std::is_floating_point_v<T>);
}
auto add(T a, T b) {
return a + b;
}
尾置 requires 从句
将 requires 从句放在 函数签名 与 函数体 之间(紧挨着花括号,在任何修饰词之后)。
template <typename T>
auto add(T a, T b) requires floating_point<T> {
return a + b;
}
模板参数约束
使用 concept 名声明模板参数来对模板参数进行约束。
template <floating_piont T>
auto add(T a, T b) {
return a + b;
}
模板参数声明缩写形式
声明模板参数的简写形式,置于 auto
左侧。
auto add(floating_point auto a, floating_point auto b) {
return a + b;
}
对于将 concept 应用到类来说,无法使用 尾置 requires 从句 和 模板参数声明缩写形式。
编写自己的 concepts
组合已定义好的 concepts
使用关系运算符 ||
和 &&
来将多个 concept 组合成一个。
template<typename T>
concept number = std::integral<T> || std::floating_point<T>;
!注意! 对于非运行(
!
)需要包裹在括号内,如:requires (!std::same_as<int>)
要求模板参数必须支持的操作
requires 表达式看起来像一个 lambda 表达式,requires
伴随在参数列表的左侧,然后是函数体。参数列表描述需要支持特定需求的模板参数,函数体描述需要支持的要求。
template <typename T>
concept addable = requires(T a, T b) { // = 右侧是一个 requires 表达式
a + b;
};
要求模板参数必须存在特定接口
template <typename T>
concept has_size = requires(T t) {
t.size();
}
要求模板参数的特定接口必须存在特定返回值类型 (即组合要求)
- 要求特定返回值的类型的接口必须包裹在一个花括号内(
{...}
) - 必须使用尾置返回类型(
->
)来表达返回值类型要求 - 返回值类型约束必须是一个
concept
(这个要求限制得比较死)
template <typename T>
concept has_size = requires(T t) {
{t.size()} -> std::same_as<size_t>;
}
要求模板参数必须存在特定的类型要求
- 存在特定的内嵌类型
- 能够实例化特定的类型模板
- 能够实例化特定的类型别名
template<typename T>
using reference = T&;
template<typename T>
concept type_requirement = requires {
typename T::value_type;
typename other<T>;
typenmae reference<T>;
};
嵌套 requires 从句
template <typename C>
concept clonable = requires(C c) {
c.clone();
requires std::same_as<C, decltype(c.clone())>; // 内嵌的 requires 从句
};
Concept 模板参数推导
template <typename Base, typename Exponent>
concept has_power = (std::integral<Exponent> || std::floating_point<Exponent>) &&
requires (Base base, Exponent exponent) {
base.power(exponent);
};
template<typename Exponent>
void print(has_power<Exponent> auto number, Exponent exponent) {
std::cout << number.power(exponent) << '\n';
}
通过匹配先推导出指数 Exponent
的类型,使得 has_power
只剩底数类型未知,再通过实参匹配 number
推导出底数类型。