C++17中如何让std::variant支持包含自身的容器类型?
实现可递归的多类型存储类型(C++17)
原代码的问题分析
- 递归类型错误:直接使用
var_type_base<var_type_base>会导致类型不完整,因为var_type_base作为模板参数传入时,对应的最终类型还未完全定义,而std::variant要求模板参数必须是完整类型。 - 变量名错误:代码中
{"vi", vi}里的vi未定义(实际应为前面的v);最后一行var_type v = m;的变量名v与前面的vector变量重复,导致编译错误。 - 类型不匹配:
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
相关产品推荐
相关产品推荐

