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

为何std::unordered_map insert的&&重载会被选中?性能差异解析

解惑:为什么非const的pair调用unordered_map::insert会选中“移动”重载?

首先要明确几个关键点:std::unordered_map<K, V>的value_type是std::pair<const K, V>,而insert的重载不止你提到的两个——还有一个完美转发的模板重载,这才是问题的核心:

template <class P>
std::pair<iterator, bool> insert(P&& value);

C++的重载决议规则会优先选择匹配度更高的重载,而非模板函数仅在和模板函数匹配度相同时才会被优先选中。结合你的问题,我们分两种场景分析:


场景1:你的描述——p是std::pair<const string, int>(即value_type)

假设我们有:

std::unordered_map<std::string, int> map;
std::pair<const std::string, int> p = {"key", 42}; // non-const左值
map.insert(p);

此时可选的重载:

  1. insert(const value_type&):需要将非const的p转换为const value_type&(添加const限定符,属于资格转换),可行但需要一次转换。
  2. 模板重载insert(P&&):模板参数P会被推导为value_type&,参数折叠后为value_type&,和p的类型完全匹配,不需要任何转换,匹配度更高。

所以模板重载会被选中。但这里并不会触发移动操作——因为std::forward<P>(p)是左值引用,内部会调用value_type的拷贝构造函数,性能应该和const版本接近。你提到的“慢一倍”更可能是混淆了p的类型,也就是下面的场景。


场景2:Matt演讲中的实际场景——p是std::pair<string, int>(非value_type)

这才是演讲里的真实情况:

// Benchmark_Slow
std::unordered_map<std::string, int> map;
std::pair<std::string, int> p = {"key", 42}; // non-const左值
map.insert(p);

// Benchmark_Fast
const std::pair<std::string, int> p_const = {"key", 42};
map.insert(p_const);

为什么Benchmark_Slow更慢?

  • 对于非const的p:
    模板重载insert(P&&)会被优先选中(因为P推导为std::pair<string, int>&,参数完全匹配p,不需要转换)。内部需要将std::pair<string, int>转换为value_type(std::pair<const string, int>):

    1. 首先构造一个临时的value_type对象:由于p是左值,会调用pair的模板转换构造函数,拷贝p的string成员(因为是左值,无法移动)。
    2. 这个临时value_type是右值,会被传递给哈希表的节点构造——但value_type的first是const string,无法移动(移动会修改原对象,而const string不允许修改),所以又会拷贝一次string。
      最终是两次string拷贝,性能自然慢一倍。
  • 对于const的p_const:
    此时有两个可行重载:

    1. 非模板重载insert(const value_type&):需要将const pair<string, int>转换为value_type(临时对象),可行。
    2. 模板重载insert(P&&):参数完全匹配const pair<string, int>&,可行。
      根据C++重载规则,非模板函数优先级高于模板函数,所以会选中insert(const value_type&)。此时仅需要一次string拷贝(构造临时value_type时),哈希表节点直接从这个临时对象拷贝构造,总拷贝次数是一次,所以更快。

总结

你描述的p是value_type的场景下,性能差异不会那么明显;而Matt演讲中的真实场景是p为非value_type的pair<string, int>,此时非const版本会触发两次string拷贝,const版本仅一次,这就是“慢一倍”的原因。核心在于模板重载的匹配优先级,以及const string无法移动导致的额外拷贝。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:30:26