如何向C++中存储std::variant的std::map添加键值对
解决无默认构造函数的std::variant存入std::map的问题
问题场景
想要使用std::map<std::string, std::variant<A, B>>存储对象,其中A、B类仅提供带参数的构造函数(无默认构造函数),尝试插入键值对时编译报错。简化示例代码如下:
class A { public: A(int x): x_(x) { std::cout << "A" << std::endl;} int x_; void print() {std::cout << 2 * x_ << std::endl;} }; class B { public: B (int x): x_(x) { std::cout << "B" << std::endl;} int x_; void print() {std::cout << 5* x_ << std::endl; } }; int main() { using var_t = std::variant<A, B>; std::map<std::string, var_t> my_map; var_t v {std::in_place_type<A>, 3}; //my_map["a"] = v; // none of these work //my_map.emplace("a", v);// my_map.insert({"a", v}); std::visit([](auto&& var) { var.print();}, my_map["a"]); }
编译错误信息
usr/local/include/c++/13.1.0/tuple:2254:9: error: use of deleted function 'std::variant<_Types>::variant() [with _Types = {A, B}]' 2254 | second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
错误原因
std::variant<A,B>没有默认构造函数——因为A和B都未定义默认构造函数,编译器会删除variant的默认构造函数。而std::map的operator[]在键不存在时,会尝试默认构造对应的值类型作为该键的默认值,这就触发了上述错误。哪怕你之前用insert或emplace插入过该键,后续误用operator[]仍可能触发这个问题。
正确实现方式
1. 替换operator[]为安全访问方法
访问已插入的元素时,改用std::map::at()方法(键不存在时抛出异常,而非尝试默认构造),或者先用find()检查键是否存在:
// 使用at()安全访问已存在的键 std::visit([](auto&& var) { var.print();}, my_map.at("a")); // 或者用find()检查后访问 auto it = my_map.find("a"); if (it != my_map.end()) { std::visit([](auto&& var) { var.print();}, it->second); }
2. 高效插入:直接在map中构造variant
不需要先构造独立的var_t对象再插入,可直接在map内部构造variant,减少不必要的拷贝:
// 直接构造A类型的variant并存入map my_map.emplace("a", std::in_place_type<A>, 3); // 直接构造B类型的variant并存入map my_map.emplace("b", std::in_place_type<B>, 4);
修复后的完整代码
class A { public: A(int x): x_(x) { std::cout << "A" << std::endl;} int x_; void print() {std::cout << 2 * x_ << std::endl;} }; class B { public: B (int x): x_(x) { std::cout << "B" << std::endl;} int x_; void print() {std::cout << 5* x_ << std::endl; } }; int main() { using var_t = std::variant<A, B>; std::map<std::string, var_t> my_map; // 直接在map内部构造variant my_map.emplace("a", std::in_place_type<A>, 3); my_map.emplace("b", std::in_place_type<B>, 4); // 使用at()安全访问 std::visit([](auto&& var) { var.print();}, my_map.at("a")); std::visit([](auto&& var) { var.print();}, my_map.at("b")); // 或者用find()检查后访问 auto it = my_map.find("a"); if (it != my_map.end()) { std::visit([](auto&& var) { var.print();}, it->second); } }
内容的提问来源于stack exchange,提问作者ManikSin
相关产品推荐
相关产品推荐

