C++20:Ranges 与 Concepts 的协同工作

在 C++20 中,Ranges 与 Concepts 两大特性相互配合,为泛型编程带来了全新的语义与效率。本文将从两者的基本概念出发,阐述它们如何共同简化代码、提升可读性,并通过示例演示如何在实际项目中将它们结合使用。

1. 何为 Range?

Range 是一种抽象的序列概念,它封装了一组可迭代元素,并提供统一的接口进行访问。相比传统的 begin()/end(),Range 让“序列”成为一个可组合、可链式操作的对象。

  • Iterator 与 Sentinel:C++20 中的 Range 用 iteratorsentinel 两个概念来定义边界,而非传统的 end()
  • View:是对 Range 的不可变变换(如 filter, transform, take 等)。
  • Adapter:可用于构造新的 Range 或 View。
#include <ranges>
#include <vector>
#include <iostream>

std::vector <int> data = {1, 2, 3, 4, 5, 6};
auto even = data | std::views::filter([](int x){ return x % 2 == 0; });

for (int v : even) std::cout << v << ' '; // 输出 2 4 6

2. Concepts 的核心

Concepts 允许我们对模板参数进行约束,提升模板的可读性与错误信息的准确性。通过 requires 关键字,可以在编译阶段检查传入类型是否满足某些属性。

template<typename T>
concept Integral = std::is_integral_v <T>;

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

3. Range 与 Concept 的结合

C++20 将 Range 与 Concept 结合,形成了 Range Concepts。通过 std::ranges::range 这个 Concept,我们可以确保一个类型真正是可迭代的,并在编译期得到验证。

3.1 只要满足 Range 就能使用算法

#include <ranges>
#include <algorithm>

template<std::ranges::range R>
auto sum(R&& r) {
    return std::accumulate(std::ranges::begin(r), std::ranges::end(r), 0);
}

3.2 通过 View 进行链式组合

#include <ranges>
#include <vector>
#include <numeric>
#include <iostream>

std::vector <int> nums = {1, 2, 3, 4, 5, 6};

auto result = std::views::filter(nums, [](int x){ return x % 2 == 0; }) // 过滤偶数
                  | std::views::transform([](int x){ return x * x; })      // 平方
                  | std::ranges::to<std::vector>();                      // 转成 vector

for (int v : result) std::cout << v << ' '; // 输出 4 16 36

4. 性能收益

  • 懒加载(Lazy evaluation):View 在使用前不执行任何操作,只有在需要迭代时才进行计算。
  • 避免中间容器:通过链式调用,可以避免生成多余的临时容器,减少拷贝与内存分配。
  • 编译时优化:Concepts 让编译器能够更准确地推导类型,进而进行更深入的优化。

5. 实战:实现一个通用的 map 函数

下面给出一个基于 Range 与 Concepts 的 map 实现,兼顾可读性与性能。

#include <ranges>
#include <utility>

template<std::ranges::range R, typename F>
auto map(R&& r, F&& f) {
    // 返回一个 transform view,延迟执行
    return std::views::transform(std::forward <R>(r), std::forward<F>(f));
}

使用示例:

std::vector <int> numbers = {1, 2, 3, 4};
auto squares = map(numbers, [](int n){ return n * n; });

for (int v : squares) std::cout << v << ' '; // 1 4 9 16

6. 小结

  • Range 提供了统一的序列抽象,支持链式变换与懒加载。
  • Concepts 在编译期对模板进行约束,提升代码安全性与错误可读性。
  • 两者结合 使得泛型代码既简洁又高效,成为现代 C++ 的强大工具。

从 C++20 开始,合理使用 Ranges 与 Concepts 能显著提升项目的可维护性和运行性能。希望本文能帮助你快速上手,并在日常编码中充分发挥这两项特性的优势。

发表评论