You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

修改说明

  1. 移除字符遍历逻辑:不再逐个检查每个码点,改为双指针遍历两个范围的区间集合
  2. 区间交集计算:通过std::max和std::min直接得到两个区间的交集起止点,仅保留有效交集
  3. 复杂度优化:时间复杂度从O(M)(M为最大码点值)降至O(N+K)(N、K为两个范围的区间数量),编译期计算成本大幅降低,即使覆盖完整UTF-16范围(0xffff)也能正常编译

内容的提问来源于stack exchange,提问作者DubbleClick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:55:09