当前位置: 首页 > news >正文

std::map::try_emplace完全详解

std::map::try_emplaceC++17 新增的关联容器成员函数,主要目标是优化 map/unordered_map 插入的性能与安全性。


1. 函数原型

template< class... Args >
std::pair<iterator,bool> try_emplace(const key_type& k, Args&&... args );template< class... Args >
std::pair<iterator,bool> try_emplace(key_type&& k, Args&&... args );template< class... Args >
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args );template< class... Args >
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args );

2. 作用与特点

  • 功能:如果 key 不存在,就原地构造一个 value(用 args... 调用 mapped_type 的构造函数);如果 key 已存在,什么都不做,只返回迭代器。

  • 关键点

    1. 避免不必要的拷贝/移动:只有在插入时才会真正构造 value
    2. insert 的区别insert 需要完整的 <key, value> 对;而 try_emplace 只需要 key + value 的构造参数。
    3. emplace 的区别emplace 在 key 存在时仍会计算参数并构造临时对象,然后发现 key 已存在再丢弃;而 try_emplace 在 key 存在时不会触发 value 构造

3. 返回值

  • 版本 1 / 2:返回 std::pair<iterator,bool>

    • first:指向新插入元素或已存在元素
    • second:true 表示插入成功,false 表示 key 已存在
  • 带 hint 版本:返回 iterator(效率优化,若 hint 合理可加速查找)


4. 示例讲解

示例 1:基础用法

#include <map>
#include <string>
#include <iostream>int main() {std::map<int, std::string> m;m.try_emplace(1, "SLAM");m.try_emplace(2, 5, 'x');  // 用 string(size, char) 构造auto [it, inserted] = m.try_emplace(1, "will_not_happen");std::cout << inserted << " " << it->second << "\n"; // 0 SLAM
}

如果 key 已存在(如 1),不会新建 string,也不会覆盖旧值。


示例 2:避免不必要的构造

#include <map>
#include <string>
#include <iostream>struct BigObj {BigObj(std::string s) { std::cout << "constructing " << s << "\n"; }
};int main() {std::map<int, BigObj> m;m.try_emplace(1, "A");  // 构造 BigObj("A")m.try_emplace(1, "B");  // key 存在,不会构造 BigObj("B")!
}

如果用 emplace,即使 key 存在,BigObj("B") 也会被构造一次再丢掉;try_emplace 避免了这笔浪费。


示例 3:完美转发构造 value

#include <map>
#include <vector>
#include <iostream>int main() {std::map<int, std::vector<int>> m;// 用参数直接构造 vectorm.try_emplace(1, 5, 10); // 构造 vector<int>(5, 10)for (auto x : m[1]) std::cout << x << " "; // 10 10 10 10 10
}

示例 4:与 hint 结合

#include <map>
#include <string>
#include <iostream>int main() {std::map<int, std::string> m;auto it = m.begin();// 提供 hint,可能优化性能it = m.try_emplace(it, 42, "hello");std::cout << it->first << " => " << it->second << "\n";
}

5. insert / emplace / try_emplace / insert_or_assign 对比

函数行为key 存在时value 构造时机
insert({k, v})插入完整 pair不插入总会先构造 v
emplace(k, args...)原地构造 value不插入总会构造一次(即使 key 存在)
try_emplace(k, args...)原地构造 value不插入仅在 key 不存在时才构造
insert_or_assign(k, v)插入或覆盖覆盖 value总会构造 v

6. 适用场景

  • 懒加载:只在 key 首次出现时才创建复杂对象(如点云、图优化的残差结构)。
  • 避免浪费:value 构造代价昂贵(如大数组、外部资源),try_emplace 可以省去无效构造。
  • 与工厂模式结合:存储大对象时,可以延迟构造到需要时。
  • 读多写少的缓存:插入时才构造,命中时只返回已有对象。

7. 小坑提醒

  1. key 的构造开销try_emplace 仍需构造/移动 key(除非用 hint 且 key 已存在)。
  2. operator[] 区别map[key] 会在 key 不存在时默认构造 value,可能导致性能浪费;而 try_emplace 可以传参数构造更高效。
  3. hint 仅是优化:给错 hint 不会错,只是回退到普通查找。
  4. 返回值处理:如果你只关心是否插入,可以忽略迭代器;如果要访问元素,记得用 it->second

总结一句话就是
try_emplacemap / unordered_map 最优的延迟构造插入方法,能避免重复构造 value,特别适合 大对象存储、昂贵构造的容器


8.综合应用示例

下面是一个 综合示例,把 insert / emplace / try_emplace / insert_or_assign 全部放在一起对比,结合一个“昂贵构造对象”的场景,直观展示它们在 性能、行为、覆盖策略 上的不同。


综合示例:地图缓存系统

点云 SLAM 地图缓存中,每个 Key 表示一个分区 ID,每个 Value 是一个“昂贵的点云对象”。

#include <map>
#include <string>
#include <iostream>// 模拟一个昂贵对象(构造时输出日志)
struct BigPointCloud {BigPointCloud(std::string name) { std::cout << "Constructing: " << name << "\n"; }BigPointCloud(const BigPointCloud& other) {std::cout << "Copying: " << "\n";}BigPointCloud(BigPointCloud&& other) noexcept {std::cout << "Moving: " << "\n";}
};int main() {std::map<int, BigPointCloud> cache;std::cout << "\n=== insert ===\n";cache.insert({1, BigPointCloud("A")});  // 总会先构造 A,再决定是否插入std::cout << "\n=== emplace ===\n";cache.emplace(1, "B");  // 即使 key=1 已存在,也会构造 B,然后丢掉std::cout << "\n=== try_emplace ===\n";cache.try_emplace(1, "C");  // key=1 已存在,不会构造 Ccache.try_emplace(2, "D");  // key=2 不存在,才构造 Dstd::cout << "\n=== insert_or_assign ===\n";cache.insert_or_assign(2, BigPointCloud("E"));  // 覆盖已有 key=2 的值,构造 E
}

输出示例

=== insert ===
Constructing: A=== emplace ===
Constructing: B=== try_emplace ===
Constructing: D=== insert_or_assign ===
Constructing: E

行为总结

  • insert:总会先构造 A(即使 key=1 已存在也会构造)
  • emplace:key=1 已存在,但仍然构造了 B(浪费)
  • try_emplace:key=1 已存在,不构造 C;key=2 不存在,才构造 D
  • insert_or_assign:key=2 已存在,强制覆盖,构造 E

从这个综合示例就能看出:

  • 如果只想在 key 不存在时插入,且避免浪费构造:try_emplace 是首选
  • 如果你想要 存在时覆盖,那就用 insert_or_assign
  • 如果你用 insert / emplace,在 key 存在时会有不必要的构造。

http://www.xdnf.cn/news/1423459.html

相关文章:

  • 从 Oracle 到 TiDB,通过ETL工具,高效实现数据拉通
  • 并发 -- JUC(java.util.concurrent) 包的简单介绍
  • NebulaAI V2.7.0发布:MCP广场正式上线!
  • FFMPEG 10BIT下 Intel b570 qsv 硬解AV1,H265视频编码测试
  • 【项目思维】贪吃蛇(嵌入式进阶方向)
  • 光学神经网络与人工智能应用
  • 【XR技术概念科普】详解6DoF:为什么它是沉浸感的关键?
  • 贝叶斯向量自回归模型 (BVAR)
  • 【Java】Redis(中间件)
  • 从API调用到效果呈现:面具特效功能在直播美颜SDK中的应用实践
  • Redis 八股
  • 中国家具百强「库斯家居」携手 企企通:启动 SRM 项目,构筑采购数字化新生态
  • Android/Java 中创建类实例的各种模式
  • nestjs 发起请求 axios
  • 3-6〔OSCP ◈ 研记〕❘ WEB应用攻击▸WEB应用枚举B
  • 【STM32】状态机(State Machine)
  • 证明与激励:Walrus 可编程数据如何通过激励可用性证明获得安全性
  • SpringBoot学习日记 Day9:响应式编程新世界探索
  • 【跨境知识】密文面单
  • Linux常用命令行大全:14个核心指令详解+实战案例
  • 多线程——线程的休眠、中断和等待
  • Markdown 语法全面指南
  • Win10系统获取网络上行流量的三种方法
  • 五、导入现有模型
  • 01 2025最新VMware虚拟机下载教程
  • Unity项目基本风格/规范
  • Linux上perf工具的使用-基础采样
  • 命名空间级别应用 Pod 安全标准
  • 从组分到涌现:系统科学视域下结构、功能与层级的辨析及在人工智能中的应用
  • 安全等保复习笔记