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

C++23中遍历无继承关系的异构对象并调用模板函数的实现方案

C++23中遍历无继承关系的异构对象并调用模板函数的实现方案

嗨,这个问题我之前在项目里也碰到过,异构类型的遍历确实是C里的一个小麻烦,不过好在C23给了我们不少顺手的工具,完全不用靠继承或者void*这种不太优雅的方式来解决。

核心思路其实很简单:既然这些变量没有继承关系,那我们就用类型安全的异构容器来把每个变量和它的名称绑定起来,然后遍历这个容器,让编译器自动为每个类型实例化模板函数。下面给你具体的实现方案:

方法一:用std::tuple+折叠表达式实现编译期遍历

首先,我们可以把每个需要处理的变量和它对应的JSON键名打包成一个条目,然后把所有条目放进std::tuple里(tuple天生就是为存储异构类型设计的)。之后用std::apply配合折叠表达式展开tuple,对每个条目调用ReadJSONkey。

具体代码示例:

#include <tuple>
#include <string>
#include <map>
// 假设已经包含ArduinoJson的头文件和ReadJSONkey的模板定义

// 先定义你的全局/成员变量
int Pins;
unsigned long SerialSpeed;
std::string NamePrefix;
std::map<std::string, uint8_t> TimeRules;

int LoadParametersFromJSON(JsonDocument& JsonDoc) {
    int err = 0;

    // 把每个变量和对应的键名绑定,存入tuple
    auto param_entries = std::make_tuple(
        // 用pair打包变量引用和键名,简洁直接
        std::pair{std::ref(Pins), "Pins"},
        std::pair{std::ref(SerialSpeed), "SerialSpeed"},
        std::pair{std::ref(NamePrefix), "NamePrefix"},
        std::pair{std::ref(TimeRules), "TimeRules"}
    );

    // 用std::apply展开tuple,折叠表达式逐个处理条目
    std::apply([&](auto&&... entries) {
        // 折叠表达式会为每个条目实例化对应的ReadJSONkey模板
        ((err += ReadJSONkey(entries.first.get(), JsonDoc, entries.second)), ...);
    }, param_entries);

    return err;
}

这里用std::ref是为了在tuple里保存变量的引用(而不是拷贝),这样ReadJSONkey才能直接修改原变量的值。展开tuple的时候,编译器会自动为每个不同的类型生成对应的ReadJSONkey特化,完全类型安全。

方法二:用自定义结构体+constexpr for循环(更易读)

如果觉得pair的可读性不够好,我们可以自定义一个结构体来封装参数和名称,再配合C++20起支持的constexpr for循环遍历tuple,代码会更清晰:

#include <tuple>
#include <string>
#include <map>

// 自定义结构体,封装参数引用和键名,语义更清晰
template <typename T>
struct JSONParam {
    T& param;
    const char* key_name;
};

// 你的变量定义
int Pins;
unsigned long SerialSpeed;
std::string NamePrefix;
std::map<std::string, uint8_t> TimeRules;

int LoadParametersFromJSON(JsonDocument& JsonDoc) {
    int err = 0;

    // 打包所有参数条目到tuple
    auto param_list = std::make_tuple(
        JSONParam{Pins, "Pins"},
        JSONParam{SerialSpeed, "SerialSpeed"},
        JSONParam{NamePrefix, "NamePrefix"},
        JSONParam{TimeRules, "TimeRules"}
    );

    // 用constexpr for循环遍历tuple的每个索引
    constexpr auto param_count = std::tuple_size_v<decltype(param_list)>;
    for (std::size_t i = 0; i < param_count; ++i) {
        std::apply([&](auto&& entry) {
            err += ReadJSONkey(entry.param, JsonDoc, entry.key_name);
        }, std::get<i>(param_list));
    }

    return err;
}

这种方式的可读性更好,一眼就能看懂每个条目的含义,而且同样是完全类型安全的,编译器会在编译期检查每个参数的类型是否和ReadJSONkey兼容。

为什么这种方案可行?

  • 完全类型安全:每个参数的类型信息都被tuple和模板结构体保存了,编译器会为每个类型实例化对应的ReadJSONkey,不会有void*那种类型丢失的问题
  • 易于维护:新增/删除参数只需要修改tuple里的条目,不需要改动循环逻辑
  • 符合C++23的现代风格:充分利用了tuple、折叠表达式、constexpr for等特性,代码简洁高效

这种方案完美解决了“遍历无继承关系的异构对象并保留类型信息”的问题,gcc14完全支持所有用到的特性。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 11:58:02