在C++20中,概念(Concepts)被引入为模板编程的强大工具。它们让我们能够在编译期对模板参数进行更细粒度的约束,从而得到更清晰的错误信息、更安全的代码以及更易维护的库。下面我们将从概念的基础语法、常用概念库、以及如何在实际项目中应用这几者进行系统讲解。
一、概念的语法与使用方式
1.1 定义概念
template <typename T>
concept Integral = std::is_integral_v <T>;
template <typename T>
concept Incrementable = requires(T a) {
{ ++a } -> std::same_as<T&>;
{ a++ } -> std::same_as <T>;
};
requires关键字后跟一个 要求列表,可以是表达式约束、类型约束或值约束。->用来指定表达式的返回类型或可转换关系。
1.2 在函数模板中使用
template <Incrementable T>
T add_one(T value) {
return ++value;
}
编译器会在实例化时检查 T 是否满足 Incrementable,不满足时会给出清晰的错误信息。
1.3 组合概念
template <typename T>
concept Number = Integral <T> || std::is_floating_point_v<T>;
template <typename T>
concept Container = requires(T c) {
typename T::value_type;
{ c.begin() } -> std::same_as<typename T::iterator>;
{ c.end() } -> std::same_as<typename T::iterator>;
};
使用 || 或 && 可以构建更复杂的约束,甚至与标准库中已有的概念组合。
二、C++20 标准库中的概念
C++20 在 `
` 头文件中预定义了许多常用概念,主要分为: | 领域 | 代表概念 | 说明 | |——|———-|——| | 数值 | `Integral`, `SignedIntegral`, `UnsignedIntegral`, `FloatingPoint` | 对基本数值类型的约束 | | 容器 | `WeaklyIncrementable`, `InputIterator`, `OutputIterator`, `ForwardIterator`, `BidirectionalIterator`, `RandomAccessIterator`, `ContiguousIterator` | 对迭代器的层级约束 | | 范围 | `Range` | 需要 `begin()` 和 `end()` 成员 | | 赋值 | `Assignable` | 可通过 `=` 赋值 | ### 2.1 典型示例:使用 `std::ranges::sort` “`cpp #include #include void sort_vector(std::vector & vec) { std::ranges::sort(vec); // 仅在 vec 是随机访问容器时编译 } “` 如果传入一个非随机访问容器,编译错误会明确指出 `sort` 的前置条件。 ## 三、实战案例:构建一个安全的矩阵乘法库 ### 3.1 需求 – 只允许整数矩阵(`Integral`)或浮点矩阵(`FloatingPoint`)。 – 行列相乘时,前矩阵列数必须等于后矩阵行数。 – 提供 `multiply` 函数模板,返回矩阵。 ### 3.2 实现 “`cpp #include #include #include template concept Number = std::integral || std::floating_point; template class Matrix { public: size_t rows, cols; std::vector data; Matrix(size_t r, size_t c) : rows(r), cols(c), data(r*c) {} T& operator()(size_t i, size_t j) { return data[i*cols + j]; } const T& operator()(size_t i, size_t j) const { return data[i*cols + j]; } }; template Matrix multiply(const Matrix& A, const Matrix& B) { if (A.cols != B.rows) throw std::invalid_argument(“Dimension mismatch”); Matrix result(A.rows, B.cols); for (size_t i = 0; i A(2, 3); Matrix B(3, 2); // 初始化数据… auto C = multiply(A, B); // C 的类型为 Matrix } “` 如果尝试使用字符串类型: “`cpp Matrix A(2, 2); Matrix B(2, 2); auto C = multiply(A, B); // 编译错误:std::string 不是 Number “` 编译器会给出明确的错误提示:`std::string` 不是 `Number` 的实例。 ## 四、总结 – **概念** 提升了模板代码的可读性与可维护性。 – **标准库中的概念** 已覆盖大部分日常使用需求。 – 在实际项目中使用概念可以提前捕获错误,减少调试成本。 通过合理使用 C++20 的概念,你可以在保证灵活性的同时,让模板代码更安全、更易理解,真正做到“编译期的类型安全”与“运行时的高性能”兼顾。