在C++20之前,模板编程往往伴随着“SFINAE”和“enable_if”等技巧,使得模板约束的写法繁琐且易出错。C++20 引入了 Concepts,提供了一种更直观、更强大的方式来描述类型的约束。下面从概念的定义、使用场景、常用标准概念以及实际编写一个通用算法的例子,来系统地展示概念在模板编程中的作用。
1. 概念(Concepts)概述
概念是一种对类型满足某种约束的声明。它的语法与模板参数相似,但作用是限制模板参数必须满足的特性。与传统的 SFINAE 机制相比,概念可以:
- 可读性更好:约束写在函数或类模板的参数列表中,直接表达意图;
- 错误信息更友好:编译器在约束不满足时给出具体的概念名称,帮助定位问题;
- 编译速度提升:编译器可以在类型检查前先判定是否满足约束,减少不必要的实例化。
2. 基本语法
template<typename T>
concept Arithmetic = std::is_arithmetic_v <T>; // 利用标准库检测
// 使用概念
template<Arithmetic T>
T add(T a, T b) { return a + b; }
注意:概念可以用
typename或class关键字,语义相同。
3. 标准库提供的常用概念
| 概念 | 作用 | 示例 |
|---|---|---|
std::integral |
整数类型 | template<std::integral T> T inc(T v) { return v+1; } |
std::floating_point |
浮点类型 | template<std::floating_point T> T square(T x){ return x*x; } |
std::input_iterator |
输入迭代器 | template<std::input_iterator It> void print(It begin, It end) |
std::ranges::range |
范围(容器或范围对象) | template<std::ranges::range R> auto sum(R&& r) |
这些概念大多在 `
` 和 “ 头文件中定义。
4. 自定义概念的写法
template<typename T>
concept Incrementable = requires(T x) {
{ ++x } -> std::same_as<T&>; // 前置递增返回自引用
{ x++ } -> std::same_as <T>; // 后置递增返回原值
};
template<Incrementable T>
T increment(T& val) { return ++val; }
上述 Incrementable 检查类型 T 是否支持前置和后置递增操作,且返回类型符合预期。
5. 典型案例:实现一个通用的 max 算法
在传统 C++ 中,我们会使用 std::max,它依赖于 `