C++ 23 新特性:范围-based 计数器的改进

在 C++ 23 标准中, 库得到了显著扩展,其中最引人注目的是对计数器(std::ranges::counting_view)的改进。之前的实现已经提供了一个轻量级的视图,用于生成从起始点到终点的递增序列,但在性能、可定制性以及与其他视图的组合方面仍有提升空间。C++ 23 的更新使得计数器视图在多种场景下更具可用性。

1. 计数器视图的核心改进

1.1 更细粒度的步长控制

旧版的 counting_view 只支持步长为 1 的整数序列。现在,它允许用户指定任何可复制且满足 std::integral 要求的步长类型,例如 int8_tint64_t,甚至是自定义的整数包装器。函数签名如下:

template<std::integral T = std::size_t>
auto counting_view(T first, T last, T step = 1);

这意味着可以更高效地处理大范围或稀疏计数,例如生成 1000 到 2000 之间每隔 5 的整数。

1.2 与 std::ranges::views::filter 的更好兼容

计数器视图现在返回一个具有可迭代特性且支持 begin()end() 的轻量级对象,可以直接与 std::ranges::views::filterstd::ranges::views::transform 等视图链式组合,而无需先转换为容器。例如:

auto evens = std::ranges::views::counting(0, 100, 1)
               | std::ranges::views::filter([](int x){ return x % 2 == 0; });

for (int n : evens) std::cout << n << ' ';

这种组合在编译时即完成,避免了中间容器的开销。

1.3 对 std::ranges::range 协议的完整支持

C++ 23 的计数器视图实现了 std::ranges::range 协议,允许在算法的 beginend 位置直接使用。它还实现了 std::ranges::sentinel 的概念,使得在 for 循环和 std::ranges::for_each 等算法中更安全、更高效。

2. 性能收益

在对比旧版计数器与新版计数器的基准测试时,主要收益体现在:

  • 无额外内存分配:计数器始终保持为一个小的结构体,不需要分配内部容器。
  • 更低的指针间接:步长的存储直接在结构体中,减少了额外的访问层级。
  • 更快的迭代速度:通过对齐和内联优化,编译器能够生成更高效的循环代码,尤其在与 std::ranges::views::transform 组合时。

3. 典型使用案例

3.1 生成斐波那契序列

尽管计数器本身不支持斐波那契,但结合 views::transform 可以轻松实现:

auto fib_seq = std::ranges::views::iota(0, 10)
                | std::ranges::views::transform([a = 0, b = 1](int){ 
                    int c = a + b;
                    a = b;
                    b = c;
                    return a;
                  });

for (auto f : fib_seq) std::cout << f << ' ';

3.2 计算大范围的素数

利用计数器与 views::filter 组合,并配合 std::ranges::cpp20::is_prime(假设存在),可以快速过滤出素数:

auto primes = std::ranges::views::counting(2, 1000000, 1)
                | std::ranges::views::filter(is_prime);

std::cout << "素数个数: " << std::ranges::distance(primes) << '\n';

4. 与现有代码的兼容性

计数器视图的 API 兼容旧版,并且通过 std::ranges::views::counting 进行访问。旧代码仍可在新标准下编译,且无需做大规模改动。建议在项目中逐步迁移到新的视图链式写法,以获得更高的可读性与性能。

5. 小结

C++ 23 对 std::ranges::counting_view 的改进,使其在步长自定义、视图组合、范围协议支持以及性能上都获得显著提升。对于需要在编译时生成数值序列的场景,新的计数器视图提供了更灵活、更高效的工具,值得在日常编码实践中尝试。

发表评论