概念(Concepts)是 C++20 引入的一项强大功能,它通过对模板参数进行约束,为泛型编程提供了更好的可读性、可维护性和编译时错误检测。本文将从概念的基本语法、实现细节以及在实际项目中的应用场景展开阐述,并给出若干实用的代码示例。
1. 概念的基本语法
概念在 C++20 中被定义为一个“约束”,其语法类似于模板,但它只能用于约束模板参数,而不能直接实例化。基本格式如下:
template <typename T>
concept Integral = std::is_integral_v <T>; // 只接受整数类型
template <Integral T>
void foo(T value) {
// ...
}
在上述示例中,Integral 是一个概念,它使用标准库中的 `std::is_integral_v
` 进行约束。`foo` 函数仅接受满足 `Integral` 的类型。
### 2. 组合概念与约束表达式
C++20 提供了多种方式来组合概念,例如 `&&`、`||`、`!`、`requires` 子句等:
“`cpp
template
concept Addable = requires(T a, T b) {
{ a + b } -> std::convertible_to
;
};
template
concept Multipliable = requires(T a, T b) {
{ a * b } -> std::convertible_to
;
};
template
requires Addable
&& Multipliable
T compute(T a, T b) {
return a + b + a * b;
}
“`
`requires` 子句允许在模板内部写更细粒度的约束,而 `requires` 子句也可以放在函数签名上,类似于 `requires Addable
&& Multipliable`。
### 3. 自定义约束的优势
1. **错误信息友好**:编译器会在约束不满足时给出清晰的错误提示,帮助快速定位问题。
2. **更高层次的抽象**:开发者可以将“行为”抽象为概念,代码阅读者可以立即理解模板所需的功能。
3. **编译时检查**:比起传统的 SFINAE,概念在编译期间提供更直观、可追踪的约束检查。
### 4. 在 STL 之中使用概念
C++ 标准库在 20 版之后大量使用概念。例如 `std::ranges::range`、`std::iterator_traits` 的 `value_type` 等都通过概念进行约束。下面给出一个使用 `std::ranges::input_range` 的例子:
“`cpp
#include
#include
#include
template
auto sum(Range&& r) {
using Value = std::ranges::range_value_t
;
Value total{};
for (auto&& elem : std::forward
(r)) {
total += elem;
}
return total;
}
int main() {
std::vector
vec{1,2,3,4,5};
std::cout
concept Printable = requires(T t) {
{ std::to_string(t) } -> std::convertible_to;
};
class Base {
public:
virtual std::string toString() const = 0;
};
class DerivedA : public Base {
public:
std::string toString() const override { return “A”; }
};
class DerivedB : public Base {
public:
std::string toString() const override { return “B”; }
};
template
void print(const T& t) {
std::cout
requires std::is_base_of_v
void print(const T& t) {
std::cout