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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 14:57:46