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

如何在std::unordered_map中存储含std::atomic的结构体作为值?

std::atomic在std::unordered_map中存储的编译问题解析

问题现象

std::atomic可以直接作为值存储在std::unordered_map中,也能通过std::make_pair存入std::pair类型的值,但以下两种场景会编译失败:

  1. 使用初始化列表向存储std::pair的unordered_map插入元素
  2. 向存储含std::atomic的自定义结构体的unordered_map插入元素

对应的示例代码:

#include <atomic>
#include <string>
#include <unordered_map>

struct LexemData {
    unsigned int id{};
    std::atomic<size_t> counter{};
};

std::unordered_map<std::string, std::atomic<size_t>> dict1;
std::unordered_map<std::string, std::pair<unsigned int, std::atomic<size_t>>> dict2;
std::unordered_map<std::string, LexemData> dict3;

int main()
{
    std::string lexem("lexem");
    
    dict2.insert({ lexem, std::make_pair(0, 0) }); // 编译成功
    dict2.insert({ lexem, {0, 0} }); // 编译失败(1)
    dict3.insert({ lexem, LexemData {0,0} });  // 编译失败(2)
}

核心原因

std::atomic是不可复制构造、不可复制赋值的类型,但它支持移动构造和移动赋值(C++11及以后标准)。编译失败的本质是触发了std::atomic的复制操作:

  1. 对于dict2.insert({ lexem, {0, 0} }):初始化列表{0,0}会先构造一个临时的std::pair对象,随后容器尝试通过复制构造将这个临时对象存入容器,而pair内的std::atomic无法被复制,导致编译错误。
  2. 对于dict3.insert({ lexem, LexemData {0,0} }):临时构造的LexemData对象会被尝试复制到容器中,结构体包含的std::atomic无法复制,因此编译失败。

而dict2.insert({ lexem, std::make_pair(0, 0) })能成功,是因为std::make_pair直接构造的pair是右值,容器会使用移动构造将其存入,避免了复制操作。

解决方案

1. 修复初始化列表插入失败(场景1)

使用emplace代替insert,直接在容器内部就地构造元素,完全避免临时对象的复制:

// 用piecewise_construct构造pair的两个成员,就地初始化
dict2.emplace(
    std::piecewise_construct,
    std::forward_as_tuple(lexem),
    std::forward_as_tuple(0, std::atomic<size_t>(0))
);

或者显式构造右值pair并移动插入:

dict2.insert({ lexem, std::pair<unsigned int, std::atomic<size_t>>(0, 0) });

2. 修复自定义结构体插入失败(场景2)

方法一:利用移动语义插入

将临时结构体转为右值,触发移动构造:

dict3.insert({ lexem, std::move(LexemData{0, 0}) });

方法二:让结构体支持移动构造(显式声明)

虽然C++编译器会为包含可移动成员的结构体自动生成移动构造函数,但显式声明可以避免编译器因其他因素(如存在自定义构造函数)不生成的情况:

struct LexemData {
    unsigned int id{};
    std::atomic<size_t> counter{};
    
    // 显式默认移动构造和移动赋值
    LexemData(LexemData&&) = default;
    LexemData& operator=(LexemData&&) = default;
};

之后即可直接用emplace就地构造元素,彻底避免复制:

dict3.emplace(lexem, 0, 0);

内容的提问来源于stack exchange,提问作者Damir Tenishev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 01:11:01