如何在运行时通过类型信息使用std::get访问std::variant
如何在运行时指定类型信息,使用
std::get访问std::variant? 以下是尝试编写的无法运行的代码:
#include <iostream> #include <string> #include <variant> class A { public: A(const int a) { va = a; // 以下代码无法编译 // variant_type = int; } A(const std::string& a) { va = a; // 以下代码无法编译 // variant_type = std::string; } // 以上代码无法编译 // type variant_type; std::variant<int, std::string> va{}; }; int main() { std::variant<int, std::string> va{"abcd"}; std::cout << std::get<1>(va) << std::endl; va = 23; std::cout << std::get<0>(va) << std::endl; const A a_obj{1}; // 以下代码无法编译 // std::get<a_obj.type>(a_obj); const A b_obj{"abc"}; // 以下代码无法编译 // std::get<b_obj.type>(b_obj); }
核心问题
std::get的模板参数(不管是类型还是索引)必须是编译时常量,你尝试用运行时的对象成员(a_obj.type)来指定,这是不允许的——模板实例化发生在编译阶段,运行时的变量值对编译器来说是未知的,所以代码编译失败。
解决方案
1. 用std::visit自动匹配类型(推荐)
std::visit会自动识别variant当前存储的类型,执行对应的操作,不需要手动指定类型或索引,代码更简洁安全。
修改后的代码:
#include <iostream> #include <string> #include <variant> class A { public: A(const int a) : va(a) {} A(const std::string& a) : va(a) {} std::variant<int, std::string> va{}; }; int main() { std::variant<int, std::string> va{"abcd"}; std::visit([](const auto& val) { std::cout << val << std::endl; }, va); // 输出abcd va = 23; std::visit([](const auto& val) { std::cout << val << std::endl; }, va); // 输出23 const A a_obj{1}; std::visit([](const auto& val) { std::cout << val << std::endl; }, a_obj.va); // 输出1 const A b_obj{"abc"}; std::visit([](const auto& val) { std::cout << val << std::endl; }, b_obj.va); // 输出abc }
如果需要更复杂的类型处理,可以定义一个结构体作为访问器:
struct VariantHandler { void operator()(int val) const { std::cout << "整数类型:" << val << std::endl; } void operator()(const std::string& val) const { std::cout << "字符串类型:" << val << std::endl; } }; // 使用时: std::visit(VariantHandler{}, a_obj.va);
2. 保存类型索引,运行时分支调用
在类A中存储当前variant的类型索引,然后通过索引分支调用std::get:
#include <iostream> #include <string> #include <variant> class A { public: A(const int a) : va(a), type_idx(0) {} A(const std::string& a) : va(a), type_idx(1) {} std::variant<int, std::string> va{}; size_t type_idx; // 0对应int,1对应std::string }; int main() { const A a_obj{1}; if (a_obj.type_idx == 0) { std::cout << std::get<0>(a_obj.va) << std::endl; } else { std::cout << std::get<1>(a_obj.va) << std::endl; } const A b_obj{"abc"}; if (b_obj.type_idx == 0) { std::cout << std::get<0>(b_obj.va) << std::endl; } else { std::cout << std::get<1>(b_obj.va) << std::endl; } }
注意:这种方式需要手动维护索引和类型的对应关系,一旦variant的类型列表修改,必须同步更新索引,容易出错。
3. 用std::holds_alternative判断类型
不需要额外保存索引,直接用std::holds_alternative检查当前存储的类型:
int main() { const A a_obj{1}; if (std::holds_alternative<int>(a_obj.va)) { std::cout << std::get<int>(a_obj.va) << std::endl; } else if (std::holds_alternative<std::string>(a_obj.va)) { std::cout << std::get<std::string>(a_obj.va) << std::endl; } const A b_obj{"abc"}; if (std::holds_alternative<int>(b_obj.va)) { std::cout << std::get<int>(b_obj.va) << std::endl; } else if (std::holds_alternative<std::string>(b_obj.va)) { std::cout << std::get<std::string>(b_obj.va) << std::endl; } }
这种方式比方案2更安全,但本质还是运行时分支判断,代码量比std::visit多。
内容的提问来源于stack exchange,提问作者Harry
相关产品推荐
相关产品推荐

