C++ 中的 constexpr 迭代器:在编译期实现序列遍历

在 C++20 之前,constexpr 的限制让我们无法在编译期遍历容器。随着 std::array 和 std::vector 的 constexpr 支持,以及 C++23 中 constexpr 迭代器的引入,编译期遍历变得可行。下面给出一个完整的实现示例,展示如何在编译期对 std::array 进行遍历,并计算其元素之和。

#include <array>
#include <iostream>
#include <utility>

namespace constexpr_iter {
    // constexpr 可迭代器包装
    template<typename T, std::size_t N, std::size_t I>
    struct iterator {
        constexpr iterator(const T(&arr)[N]) : arr(arr) {}
        constexpr const T& operator*() const { return arr[I]; }
        constexpr bool operator!=(const iterator<T, N, I+1>&) const { return I < N; }
        constexpr iterator<T, N, I+1> operator++() const { return {}; }
        const T(&arr)[N];
    };

    template<typename T, std::size_t N>
    constexpr auto begin(const T(&arr)[N]) {
        return iterator<T, N, 0>(arr);
    }

    template<typename T, std::size_t N>
    constexpr auto end(const T(&arr)[N]) {
        return iterator<T, N, N>(arr);
    }

    // 递归求和
    template<typename It, typename End, std::size_t Acc = 0>
    constexpr std::size_t sum(It it, End) {
        if constexpr (It::operator!=(End{})) {
            return sum(++It{}, End{}, Acc + *it);
        } else {
            return Acc;
        }
    }

    template<typename T, std::size_t N>
    constexpr std::size_t constexpr_sum(const T(&arr)[N]) {
        return sum(begin(arr), end(arr));
    }
}

int main() {
    constexpr std::array<int, 5> arr = {1, 2, 3, 4, 5};
    constexpr std::size_t result = constexpr_iter::constexpr_sum(arr.data());

    std::cout << "编译期求和结果: " << result << '\n';
    return 0;
}

关键点解析

  1. 迭代器包装
    constexpr_iter::iterator 把数组索引映射成一个 constexpr 迭代器。operator* 返回当前元素,operator++ 返回下一个迭代器实例,operator!= 判断是否到达终点。

  2. 递归求和
    constexpr sum 使用编译期递归来遍历迭代器。if constexpr 保证在递归结束时不再继续展开,避免无限递归。

  3. 编译期计算
    constexpr std::array 或普通 C++数组传入 constexpr_sum,在编译阶段完成求和。mainconstexpr std::size_t result 说明了这一点。

适用场景

  • 生成编译期常量:如生成哈希表的初始值、状态机的转移表等。
  • 提高运行时性能:把循环移到编译期,减少运行时开销。
  • 模板元编程替代:使用 constexpr 递归代替模板元编程,实现更易读的代码。

进一步扩展

  • 将迭代器支持任意可遍历容器(如 std::vector
  • 在 C++23 中直接使用 std::ranges::views::iotaconstexpr 结合,实现更简洁的遍历
  • 结合 consteval 进一步限制运行时调用

通过上述实现,我们展示了在 C++20 之后利用 constexpr 迭代器实现编译期遍历的完整方案,为高性能、可维护的 C++ 代码提供了新的工具。

发表评论