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

C++17中如何让std::variant支持包含自身的容器类型?

实现可递归的多类型存储类型(C++17)

原代码的问题分析

  1. 递归类型错误:直接使用var_type_base<var_type_base>会导致类型不完整,因为var_type_base作为模板参数传入时,对应的最终类型还未完全定义,而std::variant要求模板参数必须是完整类型。
  2. 变量名错误:代码中{"vi", vi}里的vi未定义(实际应为前面的v);最后一行var_type v = m;的变量名v与前面的vector变量重复,导致编译错误。
  3. 类型不匹配:vector<int>、map<string, int>等原生容器类型无法直接存入目标类型,因为目标类型的容器元素要求是自身类型而非原生类型。

正确实现代码

#include <map>
#include <string>
#include <variant>
#include <vector>
#include <utility> // 用于std::forward

// 前向声明,让容器能识别该类型
struct var_value;

// 定义variant类型,包含所有需要支持的类型,包括嵌套的vector和map
using var_variant = std::variant<
    bool,
    int,
    double,
    std::string,
    std::vector<var_value>,
    std::map<std::string, var_value>
>;

// 包装struct,持有variant实例,解决递归类型不完全的问题
struct var_value {
    var_variant data;

    // 通用构造函数,支持所有variant兼容类型的隐式转换
    template<typename T>
    var_value(T&& val) noexcept(std::is_nothrow_constructible_v<var_variant, T&&>)
        : data(std::forward<T>(val)) {}

    // 默认复制/移动构造与赋值
    var_value(const var_value&) = default;
    var_value(var_value&&) = default;
    var_value& operator=(const var_value&) = default;
    var_value& operator=(var_value&&) = default;
};

int main() {
    int i = 0;
    double d = 0.;
    std::string s = "s";
    std::vector<int> vi{0};
    std::map<std::string, int> mi;
    std::map<std::string, double> md;
    std::map<std::string, std::string> ms;
    std::map<std::string, std::vector<int>> mvi;

    // 将原生容器转换为嵌套var_value的容器类型
    std::map<std::string, var_value> m{
        {"i", i},
        {"d", d},
        {"s", s},
        {"vi", std::vector<var_value>(vi.begin(), vi.end())},
        {"mi", [&](){
            std::map<std::string, var_value> temp;
            for (const auto& pair : mi) temp[pair.first] = pair.second;
            return temp;
        }()},
        {"md", [&](){
            std::map<std::string, var_value> temp;
            for (const auto& pair : md) temp[pair.first] = pair.second;
            return temp;
        }()},
        {"ms", [&](){
            std::map<std::string, var_value> temp;
            for (const auto& pair : ms) temp[pair.first] = pair.second;
            return temp;
        }()},
        {"mvi", [&](){
            std::map<std::string, var_value> temp;
            for (const auto& pair : mvi) {
                temp[pair.first] = std::vector<var_value>(pair.second.begin(), pair.second.end());
            }
            return temp;
        }()}
    };

    var_value root = m;
    return 0;
}

关键说明

  • 递归类型处理:通过前向声明var_value,让var_variant可以提前引用vector<var_value>和map<string, var_value>。C++17开始允许标准容器使用不完全类型作为元素,因此这个写法是合法的。
  • 类型转换:原生容器类型(如vector<int>)需要转换为嵌套var_value的容器类型才能存入目标类型,示例中用lambda表达式完成了批量转换。
  • 通用构造函数:var_value的模板构造函数支持所有var_variant兼容类型的隐式转换,简化了赋值操作。

访问存储的值示例

要读取var_value中的数据,需使用std::visit处理variant的不同类型:

#include <iostream>

void print_value(const var_value& val) {
    std::visit([](const auto& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_same_v<T, bool>) {
            std::cout << std::boolalpha << v;
        } else if constexpr (std::is_arithmetic_v<T>) {
            std::cout << v;
        } else if constexpr (std::is_same_v<T, std::string>) {
            std::cout << "\"" << v << "\"";
        } else if constexpr (std::is_same_v<T, std::vector<var_value>>) {
            std::cout << "[";
            bool first = true;
            for (const auto& elem : v) {
                if (!first) std::cout << ", ";
                print_value(elem);
                first = false;
            }
            std::cout << "]";
        } else if constexpr (std::is_same_v<T, std::map<std::string, var_value>>) {
            std::cout << "{";
            bool first = true;
            for (const auto& pair : v) {
                if (!first) std::cout << ", ";
                std::cout << "\"" << pair.first << "\": ";
                print_value(pair.second);
                first = false;
            }
            std::cout << "}";
        }
    }, val.data);
}

// 在main中调用:
// print_value(root);
// std::cout << std::endl;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 05:25:38