在 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;
}
关键点解析
-
迭代器包装
constexpr_iter::iterator把数组索引映射成一个 constexpr 迭代器。operator*返回当前元素,operator++返回下一个迭代器实例,operator!=判断是否到达终点。 -
递归求和
constexpr sum使用编译期递归来遍历迭代器。if constexpr保证在递归结束时不再继续展开,避免无限递归。 -
编译期计算
constexpr std::array或普通 C++数组传入constexpr_sum,在编译阶段完成求和。main中constexpr std::size_t result说明了这一点。
适用场景
- 生成编译期常量:如生成哈希表的初始值、状态机的转移表等。
- 提高运行时性能:把循环移到编译期,减少运行时开销。
- 模板元编程替代:使用 constexpr 递归代替模板元编程,实现更易读的代码。
进一步扩展
- 将迭代器支持任意可遍历容器(如
std::vector) - 在 C++23 中直接使用
std::ranges::views::iota与constexpr结合,实现更简洁的遍历 - 结合
consteval进一步限制运行时调用
通过上述实现,我们展示了在 C++20 之后利用 constexpr 迭代器实现编译期遍历的完整方案,为高性能、可维护的 C++ 代码提供了新的工具。