C++20 在标准库中引入了 Concepts,提供了一种新的语法与机制来约束模板参数。相比传统的 SFINAE 或 enable_if 技术,Concepts 更直观、可读性更强,并能在编译阶段提供更友好的错误信息。下面我们从概念的基本语法开始,逐步演示如何使用 Concepts 进行函数、类和模板的约束,并通过实战案例展示其优势。
1. 何为 Concept
Concept 是一组针对类型或表达式的要求(约束)。在模板参数列表中通过 requires 子句或 typename 前置关键字来指定这些约束。Concept 的定义与实现非常灵活,可以是函数、表达式、类型成员或组合多种约束。
template<typename T>
concept Incrementable = requires(T x) {
{ ++x } -> std::same_as<T&>;
{ x++ } -> std::same_as <T>;
};
上述 Concept 要求 T 必须支持前置和后置自增,并且返回值分别为 T& 与 T。
2. 语法细节
-
Concept 定义
template<typename T> concept Name = /* 约束条件 */;条件可以是:
- 语法上合法的表达式
- 类型成员存在
- 逻辑组合(
&&、||、!) - 嵌套调用其他 Concept
-
在模板中使用
template<Incrementable T> void inc(T &t) { ++t; }或者使用
requires语句块:template<typename T> requires Incrementable <T> void inc(T &t) { ++t; } -
默认参数化
Concept 可以用作模板参数默认值,例如:template<template<typename> class Container, typename T = int> requires std::default_initializable <T> class Wrapper { /* ... */ };
3. 示例:实现一个安全的 add 函数
#include <concepts>
#include <iostream>
#include <vector>
template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::same_as <T>;
};
template<Addable T>
T add(const T& a, const T& b) {
return a + b;
}
int main() {
std::cout << add(5, 7) << '\n'; // 输出 12
std::cout << add(3.14, 2.71) << '\n'; // 输出 5.85
// add("Hello", "World"); // 编译错误,字符串不满足 Addable
}
4. 与 SFINAE 对比
| 特点 | Concepts | SFINAE / enable_if |
|---|---|---|
| 可读性 | 高 | 低 |
| 错误信息 | 更清晰 | 通常模糊 |
| 语法 | 简洁 | 复杂 |
| 约束表达 | 直接 | 通过 decltype/std::enable_if_t 等间接 |
5. 进阶:组合多个 Concept
template<typename T>
concept Arithmetic = std::integral <T> || std::floating_point<T>;
template<typename T>
concept Hashable = requires(T a) {
{ std::hash <T>{}(a) } -> std::convertible_to<std::size_t>;
};
template<typename T>
concept HashableArithmetic = Hashable <T> && Arithmetic<T>;
template<HashableArithmetic T>
T square_hash(T value) {
return std::hash <T>{}(value * value);
}
6. 性能与编译时间
Concepts 本身不会导致运行时开销,它们仅在编译阶段检查约束。虽然概念的定义可能会增加编译时间,但在大型项目中,这种额外的编译成本通常被更好的错误定位与可维护性所抵消。
7. 实战案例:使用 Concept 约束自定义排序器
#include <concepts>
#include <functional>
#include <vector>
#include <algorithm>
template<typename T>
concept Comparable = requires(T a, T b) {
{ a < b } -> std::convertible_to<bool>;
};
template<Comparable T>
class Sorter {
public:
void sort(std::vector <T>& data, std::function<bool(const T&, const T&)> comp = std::less<>{}) {
std::sort(data.begin(), data.end(), comp);
}
};
int main() {
Sorter <int> intSorter;
std::vector <int> nums = {4, 2, 9, 1};
intSorter.sort(nums);
for (int n : nums) std::cout << n << ' '; // 输出 1 2 4 9
}
8. 结语
C++20 Concepts 为模板编程提供了更具表达力和可维护性的约束机制。通过合理使用 Concepts,既能让代码更易读,又能在编译阶段提前捕获错误。随着标准化的推进,Concepts 已成为现代 C++ 开发的重要工具,值得每一位 C++ 开发者深入掌握。
祝你在使用 Concepts 的旅程中收获更多的乐趣与效率。