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

C++17中实现依赖输入内容的constexpr数组[N]转数组[M]方法问询

C++17下实现无内存浪费的constexpr字符串转义函数

我需要把以下string版本的转义函数:

string escape(const string& s)
{
    string r = "";
    for(char c:s)
    {
        if(c=='"') 
        {
            r+='\\'; r+=c;
        }
        else r+=c;
    }
    return r;
}

转换成类似这样的constexpr版本:

constexpr const char* escape(const char* s) { ??? }

这个需求是为了演示**通用constexpr数组[N]转数组[M]**的方案,其中M的大小完全取决于输入数组[N]的内容。我的平台对RAM要求很高,希望尽可能把数据放到只读段,同时保持代码清晰,而且不想为这个问题切换到C20,所以想知道C17下是否可行。

我试过基于输出上限的方案:输入有N个字符时,输出最大长度是2*N(最坏情况是全引号的字符串),代码大概是这样:

template<int N>
struct static_str { char data[N]; };

template<int N>
constexpr static_str<2*N> escape(static_str<N> s)
{
    static_str<2*N> r;
    int k=0;
    for(int i=0;i<N;i++)
    {
        if(s.data[i]=='"') { r.data[k++]='\\'; }
        r.data[k++] = s.data[i];
    }
    return r;
}

但这个方法会浪费内存,我有几个思路但都受限于C++17的规则:

  • 可以预计算输出长度expected_len(s) = strlen(s) + <引号数量>,但没法创建static_str<expected_len(s)>类型的变量,编译器提示“expected_len(s)不是常量”;
  • 尝试把上述escape函数得到的2*N大小的输出截断到expected_len(s),但不知道怎么操作不违反模板机制;
  • 如果输出大小难以预先确定的话,该怎么处理?

解决方案:利用constexpr计算长度+模板辅助结构

在C++17中,虽然不能直接用运行时值作为模板参数,但可以通过编译期常量计算结合模板非类型参数实现无内存浪费的constexpr转义,同时确保数据存入只读段。

步骤1:实现constexpr长度计算函数

先写一个编译期就能算出转义后长度的函数:

constexpr size_t escaped_len(const char* s) {
    size_t len = 0;
    while (*s) {
        len += (*s == '"') ? 2 : 1;
        ++s;
    }
    return len + 1; // 加上字符串终止符'\0'
}

步骤2:用模板结构体生成精确大小的转义字符串

通过模板结构体,将输入字符串作为编译期非类型参数,同时自动推导转义后的长度,在结构体内部完成转义逻辑:

template<const char* S, size_t Len = escaped_len(S)>
struct escaped_string {
    static constexpr char value[Len];
};

// 实现结构体的静态成员(编译期初始化)
template<const char* S, size_t Len>
constexpr char escaped_string<S, Len>::value[Len] = []() {
    char result[Len] = {};
    size_t idx = 0;
    for (size_t i = 0; S[i]; ++i) {
        if (S[i] == '"') {
            result[idx++] = '\\';
        }
        result[idx++] = S[i];
    }
    result[idx] = '\0'; // 确保字符串终止
    return result;
}();

步骤3:使用方式

这样就能在编译期生成刚好大小的转义字符串,且存储在只读段:

// 定义编译期输入字符串
constexpr const char input[] = "Hello \"World\"!";

// 获取编译期转义后的结果
constexpr const char* output = escaped_string<input>::value;

// 编译期验证结果正确性
static_assert(output[6] == '\\');
static_assert(output[7] == '"');

针对问题的解答

  1. 动态长度作为模板参数的问题:C++17不允许直接用函数返回值作为模板参数,但把字符串作为模板非类型参数后,escaped_len(S)是编译期可确定的常量表达式,可以作为默认模板参数使用,完美绕过限制。
  2. 截断输出的问题:上述方案直接生成精确长度的数组,从根源避免了内存浪费,不需要额外截断操作。
  3. 输出大小难以预测的情况:只要输入是编译期已知的constexpr字符串,就能通过constexpr函数计算出精确长度;如果输入是运行时字符串,C++17无法在编译期处理,只能使用上限方案或动态分配,但你的需求是存入只读段,因此输入必须是编译期常量。

适配static_str的版本

如果要兼容你之前定义的static_str结构,可以修改为以下实现:

template<int N>
struct static_str { char data[N]; };

// 计算static_str的转义后长度
template<int N>
constexpr size_t escaped_len(const static_str<N>& s) {
    size_t len = 0;
    for (int i = 0; s.data[i]; ++i) {
        len += (s.data[i] == '"') ? 2 : 1;
    }
    return len + 1;
}

// 辅助结构体
template<typename StaticStr, size_t Len = escaped_len(StaticStr{})>
struct escaped_static_str;

template<int N, size_t Len>
struct escaped_static_str<static_str<N>, Len> {
    static constexpr static_str<Len> value = []() {
        static_str<Len> result{};
        size_t idx = 0;
        for (int i = 0; static_str<N>{}.data[i]; ++i) {
            if (static_str<N>{}.data[i] == '"') {
                result.data[idx++] = '\\';
            }
            result.data[idx++] = static_str<N>{}.data[i];
        }
        result.data[idx] = '\0';
        return result;
    }();
};

// 使用示例
constexpr static_str<14> input = {"Hello \"World\"!"};
constexpr auto output = escaped_static_str<decltype(input)>::value;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:00:09