第4章 详细介绍概念、需求和约束¶
目录¶
4.1 约束¶
这里的约束其实就是对泛型参数
的要求, 可以约束函数模板, 类模板, 变量模板和别名模板. 类似以下的例子
template <typename T>
concept MyConcept = requires(T t) {
{ t + 1 } -> std::convertible_to<int>;
{ t - 1 } -> std::convertible_to<int>;
{ t * 2 } -> std::convertible_to<int>;
{ t / 2 } -> std::convertible_to<int>;
};
template <typename T>
requires MyConcept<T>
void foo(T value) {
fmt::print("Value: {}\n", value);
}
4.2 需求项¶
requires
子句使用关键字requires
和编译时布尔表达式来限制模板的可用性. 其中, 布尔表达式可以是:
+ 编译时的布尔表达式(编译时变量或编译时函数, 由constexpr
或constinit
定义)
+ 概念
+ requires表达式
template <typename T>
requires(sizeof(T) > 4) && requires { typename T::value_type; }
void bar(T value) {}
void test_bar() {
std::vector<int> vec = {1, 2, 3};
bar(vec); // Ok, std::vector<int> has value_type
}
4.3 特别的布尔表达式¶
其实这里强调的是requires表达式的编译期特性, 如编译时变量/编译时函数.
namespace special_requires_expr {
template <typename T, std::size_t N>
requires(N > 0 && (N % 16) == 0 && std::same_as<T, int>)
void foo(T (&arr)[N]) {
fmt::print("Array size: {}\n", N);
}
void test_foo() {
int arr[16] = {0};
foo(arr); // Ok, N is 16, T is int
}
}
一些常使用的概念:
+ 判断类型T
是否是指针: std::is_pointer_v<T>
+ 判断类型T
是否是nullptr: std::same_as<T, nullptr_t>
+ 判断类型T
能否转为字符串: std::convertible_to<T, std::string>
, 也可以使用std::is_convertible_v<T>
+ 虚空创建一个类型为T的对象: std::declare<T>()
4.4 需求表达式¶
语法糖, 其实就是允许requires
之后跟着大量的expression
, 注意这里的requires
表达式的写法
template <typename T>
concept myconcept = requires {
typename T::value_type::first_type;
typename T::value_type::second_type;
};
template <typename T>
requires requires {
typename T::value_type::first_type;
typename T::value_type::second_type;
}
void func(const T &) {
// ...
}
void test_func() {
std::map<int, std::string> map;
func(map); // Ok, std::map<int, std::string> has value_type with first_type
// and second_type
}
4.5 概念详解¶
概念的定义:
- 概念不表示代码, 没有类型、存储、生命周期活与对象相关的其他属性
- 概念不必声明为内联的,可以隐士内联
- 概念可以作为类型约束
- 概念是给约束命名的唯一方法
非类型模板参数(NTTP)也可以作为概念(concept)¶
template<auto val>
concept LessThan10 = val < 10;
// 要求是2的幂
template<auto val>
concept PowerOf2 = std::has_single_bit(static_cast<unsigned>(val));
template<typename T, auto val>
requires PowerOf2<val>
struct memory {};
// 不能这么写, 这种写法的意思是限制它的类型, 而不是值
template<typename T, PowerOf2 auto val>
struct memory {};