C++20 概念(Concepts)入门与实战

概念(Concepts)是 C++20 为模板编程带来的重要新特性,它们通过在编译时对类型参数做约束,使代码更具可读性、可维护性,并能在编译阶段捕获错误。本文将从概念的基本定义、语法到实际使用场景展开介绍,并结合完整代码示例帮助读者快速上手。

1. 概念的基本语法

template<typename T>
concept Integral = std::is_integral_v <T>;
  • template<typename T>:模板参数声明。
  • concept Integral:概念名称。
  • `std::is_integral_v `:约束表达式,返回布尔值。若为 `true`,类型满足该概念。

使用概念时,可以在模板参数后直接写约束:

template<Integral T>
T add(T a, T b) {
    return a + b;
}

如果传入非整数类型,编译器会给出明确的错误信息。

2. 组合与命名约束

概念之间可以用逻辑运算符组合,形成更复杂的约束。

template<typename T>
concept Arithmetic = Integral <T> || std::is_floating_point_v<T>;

template<Arithmetic T>
T multiply(T a, T b) {
    return a * b;
}

约束别名

C++20 允许给约束起别名,提升可读性:

template<typename T>
concept Arithmetic = Integral <T> || std::is_floating_point_v<T>;

template<Arithmetic T>
T power(T base, unsigned int exp);

3. 约束表达式中的逻辑

  • &&:逻辑与
  • ||:逻辑或
  • !:逻辑非
  • ->:返回类型约束(用于 requires 语句)
template<typename T>
concept ValidContainer = requires(T a) {
    a.begin();
    a.end();
    { *a.begin() } -> std::same_as<typename T::value_type&>;
};

上述约束检查一个容器是否满足 begin()end() 并且 *begin() 的返回类型与 value_type& 相同。

4. 需要注意的编译器支持

  • GCC 10+、Clang 10+、MSVC 19.29+ 已基本支持概念。
  • 若使用旧编译器,需开启 -std=c++20 或等效选项。

5. 实战案例:通用排序函数

#include <iostream>
#include <vector>
#include <algorithm>
#include <concepts>
#include <type_traits>

// 1. 定义概念
template<typename T>
concept Arithmetic = std::is_arithmetic_v <T>;

template<typename Container>
concept RandomAccessContainer = requires(Container c) {
    typename Container::value_type;
    { c.begin() } -> std::same_as<typename Container::iterator>;
    { c.end() } -> std::same_as<typename Container::iterator>;
    { *c.begin() } -> std::same_as<typename Container::value_type&>;
};

// 2. 约束版排序
template<RandomAccessContainer C>
void generic_sort(C& container) {
    std::sort(container.begin(), container.end());
}

// 3. 使用示例
int main() {
    std::vector <int> v = { 5, 2, 9, 1, 5, 6 };
    generic_sort(v);

    std::cout << "Sorted vector: ";
    for (int n : v) std::cout << n << ' ';
    std::cout << '\n';
    return 0;
}

说明

  • RandomAccessContainer 约束确保容器具备随机访问迭代器,满足 std::sort 的要求。
  • 若传入 std::list,编译器会报错,提示不满足约束。
  • 使用概念后,错误信息更直观,例如“容器必须是随机访问容器”。

6. 进阶:概念与模板偏特化

概念可以与模板偏特化配合使用,实现更细粒度的行为控制。

template<typename T, typename Enable = void>
struct Printer { /* default */ };

template<typename T>
struct Printer<T, std::enable_if_t<Integral<T>>> {
    static void print(T value) { std::cout << "Integral: " << value << '\n'; }
};

template<typename T>
struct Printer<T, std::enable_if_t<std::is_floating_point_v<T>>> {
    static void print(T value) { std::cout << "Floating: " << value << '\n'; }
};

使用概念简化上述实现:

template<Integral T>
struct Printer <T> {
    static void print(T value) { std::cout << "Integral: " << value << '\n'; }
};

template<std::floating_point T>
struct Printer <T> {
    static void print(T value) { std::cout << "Floating: " << value << '\n'; }
};

7. 小结

  • 概念:在编译时对模板参数进行约束,提升错误诊断。
  • 语法concept 关键字,requires 子句,逻辑运算符。
  • 优势:更易读、易维护、编译期错误更明确。
  • 实践:可用于容器、数值类型、迭代器等多种场景。

掌握概念后,你的模板代码将更加健壮,也更贴近自然语言的表达,极大提升 C++20 开发体验。祝编码愉快!

发表评论