使用emplace函数插入std::unordered_map时出现编译错误,operator[]重载则正常
问题原因分析
你遇到的编译错误核心原因是**std::unique_ptr不可拷贝**,而emplace和operator[]在处理元素构造时的逻辑完全不同:
1. operator[]为什么能正常工作
当你调用mp["def"]时,unordered_map的operator[]会执行以下步骤:
- 检查键"def"是否存在,如果不存在,会默认构造一个
A对象插入到容器中 - 返回这个
A对象的引用
整个过程只用到了A的默认构造函数,而A的默认构造会默认初始化它的unique_ptr成员,完全不需要拷贝操作,所以编译没问题。
2. emplace为什么编译失败
你写的mp.emplace("abc", A()),编译器会尝试把这两个参数传递给std::pair<const std::string, A>的构造函数。这里的A()是一个临时对象,编译器会尝试用拷贝构造的方式把这个临时A放到pair里,但你的A类包含std::unique_ptr<B>——这个类型没有公开的拷贝构造函数,只有移动构造函数,所以模板参数推导时无法找到合适的构造方式,最终触发编译错误。
解决方法
有几种方式可以修复这个问题:
方法一:使用std::move转移临时对象的所有权
把临时的A转为右值,让pair使用移动构造来构造A成员:
mp.emplace("abc", std::move(A()));
方法二:直接在emplace中构造A(推荐)
利用emplace的特性,直接传递A的构造参数(这里A是默认构造,所以不需要额外参数),让容器在内部直接构造A,完全避免拷贝/移动:
mp.emplace(std::piecewise_construct, std::forward_as_tuple("abc"), std::forward_as_tuple());
piecewise_construct允许我们分别为pair的两个成员(键和值)传递构造参数,这里std::string用"abc"构造,A用默认构造。
方法三:使用try_emplace(C++17及以上)
如果你的编译器支持C++17,try_emplace是更直观的选择,它可以直接指定键和值的构造参数,而且只有当键不存在时才会构造对象:
mp.try_emplace("abc");
验证代码
修改后的完整代码示例:
#include <iostream> #include <unordered_map> #include <memory> #include <tuple> // 方法二需要这个头文件 class B { public: B() = default; ~B() = default; }; class A { public: A() = default; ~A() = default; std::unique_ptr<B> b_ptr; }; int main() { std::unordered_map<std::string, A> mp; // 方法一:std::move mp.emplace("abc", std::move(A())); // 方法二:piecewise_construct mp.emplace(std::piecewise_construct, std::forward_as_tuple("def"), std::forward_as_tuple()); // 方法三:try_emplace(C++17+) mp.try_emplace("ghi"); auto& a = mp["jkl"]; }
内容的提问来源于stack exchange,提问作者Harry
相关产品推荐
相关产品推荐

