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

C++17模板函数实现轻量JSON编码器时扩展嵌套对象的间接递归问题求解

解决C++17模板相互递归问题,实现嵌套JSON编码器

这确实是C++模板编程里典型的相互递归依赖问题——append需要调用tuple版的toString,而tuple版的toString又需要调用append,编译器找不到对方的定义就会报错。不过咱们有个非常简洁的解决方案:模板前置声明,因为模板调用只需要看到声明,不需要立即看到完整定义。

修改后的完整代码

#include <string>
#include <tuple>
#include <type_traits>
#include <utility> // 用于std::forward

namespace {
using std::string;

// 先前置声明两个相互依赖的模板,解决递归问题
template <typename... Args>
std::string append(Args&&... args);

template<typename ... Args>
std::string toString(std::tuple<Args...>&& t); // 用转发引用避免拷贝

// 基础类型的toString实现
template<typename T> 
string toString(T&& t){
    if constexpr (std::is_arithmetic_v<std::decay_t<T>>) {
        return std::to_string(t);
    }
    else if constexpr (std::is_same_v<string, std::remove_reference_t<T>>) {
        // 这里可以加个简单的字符串转义逻辑,避免JSON无效(可选)
        return "\"" + t + "\"";
    }
    else if constexpr (std::is_same_v<std::decay_t<T>, std::tuple<std::decay_t<Args>...>>) {
        return toString(std::forward<T>(t));
    }
    else if constexpr (std::is_convertible_v<T, string>) {
        return "\"" + string(std::forward<T>(t)) + "\"";
    }
    else {
        // 对于无法转成字符串的类型,可以静态断言报错,更安全
        static_assert(std::is_convertible_v<T, string>, "Unsupported type for JSON encoding");
        return "";
    }
}

// 两个参数的append基础版
template <typename Arg1, typename Arg2>
std::string append(Arg1&& arg1, Arg2&& arg2) {
    return toString(std::forward<Arg1>(arg1)) + ":" + toString(std::forward<Arg2>(arg2));
}

// 可变参数的append递归版
template <typename Arg1, typename Arg2, typename... Args>
std::string append(Arg1&& arg1, Arg2&& arg2, Args&&... moreArgs) {
    return toString(std::forward<Arg1>(arg1)) + ":" + toString(std::forward<Arg2>(arg2)) 
           + "," + append(std::forward<Args>(moreArgs)...);
}

// tuple的toString实现——现在可以正常调用append了
template<typename ... Args>
std::string toString(std::tuple<Args...>&& t) {
    std::string s = std::apply(append<Args&&...>, std::move(t));
    return "{" + s + "}";
}
}

template <typename... Args>
static std::string encodeJson(Args&&... args) {
    return "{" + append(std::forward<Args>(args)...) + "}";
}

关键修改点说明

  1. 前置声明:在匿名命名空间开头先声明append和tuple版的toString,这样编译器在处理基础版toString和append时,知道这些模板是存在的,后续会找到定义。
  2. 完美转发:把参数改成转发引用(T&&),并使用std::forward传递参数,避免不必要的拷贝,提升效率。
  3. 类型安全增强:添加了std::is_convertible_v判断和静态断言,避免传入无法编码的类型。

测试用例

#include <iostream>

int main() {
    auto json = encodeJson(
        "key", "hello world",
        "num", 42,
        "nested", std::make_tuple("subkey", "subval", "subnum", 3)
    );
    std::cout << json << std::endl;
    // 输出:{"key":"hello world","num":42,"nested":{"subkey":"subval","subnum":3}}
    return 0;
}

额外优化建议

  • 字符串转义:如果你的场景中可能出现带双引号、反斜杠的字符串,可以给字符串类型的toString添加转义逻辑,比如把"替换成\",把\替换成\\,避免生成无效JSON。
  • 数组支持:如果需要编码JSON数组,可以添加对std::vector或其他容器的toString重载,格式为[value1, value2, ...]。
  • constexpr支持:如果需要编译期生成JSON,可以尝试把函数改成constexpr(C++20及以上支持更好)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 14:42:47