C++23 constexpr尺寸限制:迭代至0x5000触发常量表达式错误
解决consteval中循环范围过大导致的C2131编译错误
问题描述
当前代码在max_codepoint设为0x4000时能正常运行并输出预期的字符范围交集,但将其修改为0x5000(或0xffff以覆盖完整UTF-16范围)时,会触发编译错误:
ConsoleApplication1.cpp(149, 30): [C2131] expression did not evaluate to a constant
原代码如下:
#include <array> #include <print> #include <vector> using ImWchar = unsigned short; consteval std::vector<ImWchar> ConstGetGlyphRangesLatin() { return { 0x0020, 0x00FF, // Basic Latin + Latin Supplement }; } consteval std::vector<ImWchar> ConstGetGlyphRangesGW() { return { 0x20, 0x7f, 0xa1, 0xff, 0x100, 0x180, 0x0391, 0x0460, 0x2010, 0x266b, 0x3000, 0x3020, 0x3041, 0x3100, 0x3105, 0x312a, 0x3131, 0x318f, 0xac00, 0xd7a4, 0x4e00, 0x9fa6, 0xf900, 0xfa6b, 0xff01, 0xffe7 }; } consteval bool in_range(const ImWchar c, const std::vector<ImWchar>& range) { for (size_t i = 0; i < range.size() - 1; i += 2) { if (c >= range[i] && c <= range[i + 1]) return true; } return false; }; consteval std::vector<ImWchar> find_glyph_range_intersection(const std::vector<ImWchar>& range1, const std::vector<ImWchar>& range2) { if (range1.empty() || range2.empty()) return {}; std::vector<ImWchar> intersection; wchar_t start = 0; bool in_intersection = false; constexpr auto max_codepoint = 0x4000; for (ImWchar c = 0; c <= max_codepoint; ++c) { const bool in_both = in_range(c, range1) && in_range(c, range2); if (in_both && !in_intersection) { start = c; in_intersection = true; } else if (!in_both && in_intersection) { intersection.push_back(start); intersection.push_back(c - 1); in_intersection = false; } } if (in_intersection) { intersection.push_back(start); intersection.push_back(max_codepoint); } intersection.push_back(0); // Null-terminate the range return intersection; } template <typename T, size_t N> consteval std::array<T, N> vec_to_array(const std::vector<T>& vec) { std::array<T, N> arr = {}; std::copy(vec.begin(), vec.end(), arr.begin()); return arr; } consteval auto get_intersection_array() { const auto latin = ConstGetGlyphRangesLatin(); const auto gw = ConstGetGlyphRangesGW(); const auto intersect = find_glyph_range_intersection(latin, gw); return vec_to_array<ImWchar, 1'000>(intersect); } int main() { constexpr std::array arr = get_intersection_array(); for (auto c : arr) { if (c != 0) std::print("{}, ", c); } }
问题原因
错误源于consteval函数的编译期计算限制。原实现通过遍历每个字符判断是否在两个范围中,当max_codepoint增大到0x5000或0xffff时,循环次数暴增,超出了编译器对常量表达式计算复杂度的阈值,导致无法在编译期完成计算。
解决方案:优化区间交集计算逻辑
放弃遍历每个字符的低效方式,改为直接对两个范围的区间进行交集计算,仅处理区间的起止点,大幅降低计算复杂度。
修改后的代码如下:
#include <array> #include <print> #include <vector> #include <algorithm> using ImWchar = unsigned short; consteval std::vector<ImWchar> ConstGetGlyphRangesLatin() { return { 0x0020, 0x00FF, // Basic Latin + Latin Supplement }; } consteval std::vector<ImWchar> ConstGetGlyphRangesGW() { return { 0x20, 0x7f, 0xa1, 0xff, 0x100, 0x180, 0x0391, 0x0460, 0x2010, 0x266b, 0x3000, 0x3020, 0x3041, 0x3100, 0x3105, 0x312a, 0x3131, 0x318f, 0xac00, 0xd7a4, 0x4e00, 0x9fa6, 0xf900, 0xfa6b, 0xff01, 0xffe7 }; } // 直接计算两个区间集合的交集 consteval std::vector<ImWchar> find_glyph_range_intersection(const std::vector<ImWchar>& range1, const std::vector<ImWchar>& range2) { if (range1.empty() || range2.empty()) return {}; std::vector<ImWchar> intersection; size_t i = 0, j = 0; while (i < range1.size() && j < range2.size()) { ImWchar start1 = range1[i], end1 = range1[i+1]; ImWchar start2 = range2[j], end2 = range2[j+1]; // 计算当前两个区间的交集起止点 ImWchar intersect_start = std::max(start1, start2); ImWchar intersect_end = std::min(end1, end2); if (intersect_start <= intersect_end) { // 仅保留有效的交集区间 intersection.push_back(intersect_start); intersection.push_back(intersect_end); } // 推进结束值较小的区间指针,继续寻找下一个可能的交集 if (end1 < end2) { i += 2; } else { j += 2; } } intersection.push_back(0); // 以空值终止范围 return intersection; } template <typename T, size_t N> consteval std::array<T, N> vec_to_array(const std::vector<T>& vec) { std::array<T, N> arr = {}; std::copy(vec.begin(), vec.end(), arr.begin()); return arr; } consteval auto get_intersection_array() { const auto latin = ConstGetGlyphRangesLatin(); const auto gw = ConstGetGlyphRangesGW(); const auto intersect = find_glyph_range_intersection(latin, gw); return vec_to_array<ImWchar, 1'000>(intersect); } int main() { constexpr std::array arr = get_intersection_array(); for (auto c : arr) { if (c != 0) std::print("{}, ", c); } }
修改说明
- 移除字符遍历逻辑:不再逐个检查每个码点,改为双指针遍历两个范围的区间集合
- 区间交集计算:通过
std::max和std::min直接得到两个区间的交集起止点,仅保留有效交集 - 复杂度优化:时间复杂度从O(M)(M为最大码点值)降至O(N+K)(N、K为两个范围的区间数量),编译期计算成本大幅降低,即使覆盖完整UTF-16范围(
0xffff)也能正常编译
内容的提问来源于stack exchange,提问作者DubbleClick
相关产品推荐
相关产品推荐

