将值分配给地图地图的最有效方法

时间:2015-01-07 15:00:15

标签: c++11

给出如下地图的地图:

  std::map<unsigned int, std::map<std::string, MyBase*>> m_allMyObjects;

插入/添加/&#34; emplace&#34;最有效的方式是什么?给定unsigned intstd::string考虑优化的m_allMyObjects元素(在现代编译器上)?

然后检索元素的最有效方法是什么?

m_allMyObjects将来可能包含多达100个&#39; 000个元素。

1 个答案:

答案 0 :(得分:3)

关于如何有效地插入地图的常识和民间传说(通常告诉你要避免operator[]并且更喜欢闪亮的新emplace)考虑构建和复制值的成本在地图中。在您的情况下,这些值是普通指针,几乎可以免费复制,并且编译器可以积极地优化复制指针。

另一方面,实际上你确实有一个昂贵的对象,即std::string类型的 key 。您需要注意密钥的副本(移动或复制)以确定性能。显然,对于树查找,您已经需要字符串对象,即使您将其作为char *,因为没有插入函数通过键的类型进行模板化。这意味着要查找要插入的位置,可以使用一个特定的std :: string对象,但是一旦创建了映射节点,映射中的新std :: string对象就会从中进行复制初始化(可能已移动)。避免超出单一副本/移动的所有内容应该是您的目标。

示例时间!

#include <map>
#include <cstdio>

struct noisy {
  noisy(int v) : val(v) {}
  noisy(const noisy& src) : val(src.val) { std::puts("copy ctor"); }
  noisy(noisy&& src) : val(src.val)      { std::puts("move ctor"); }
  noisy& operator=(const noisy& src)
  { val = src.val; std::puts("copy assign"); return *this; }
  noisy& operator=(noisy&& src)
  { val = src.val; std::puts("move assign"); return *this; }
  int val;
};

bool operator<(const noisy& a, const noisy& b)
{
  return a.val < b.val;
}

int main(void)
{
  std::map<noisy,int> m;
  std::puts("Operator[]");
  m[noisy(1)] = 3;
  std::puts("insert/make_pair");
  m.insert(std::make_pair(noisy(2), 3));
  std::puts("insert/make_pair/ref");
  m.insert(std::make_pair<noisy&&,int>(noisy(3), 3));
  std::puts("insert/pair/ref");
  m.insert(std::pair<noisy&&,int>(noisy(4), 3));
  std::puts("emplace");
  m.emplace(noisy(5), 3);
}

使用g ++ 4.9.1编译,-std = c ++ 11,-O2,结果为

Operator[]
move ctor
insert/make_pair
move ctor
move ctor
insert/make_pair/ref
move ctor
move ctor
insert/pair/ref
move ctor
emplace
move ctor

其中显示:避免创建包含密钥副本的中间对的所有内容!请注意,std :: make_pair永远不会创建包含引用的对,即使它可以通过引用获取参数!每当您传递包含密钥副本的对时,密钥都会被复制到对中,然后再复制到地图中。

MarkMB建议的表达式,即m[int_k][str_k] = ptr,非常好,可能产生最佳代码。第一个索引(int_k)没有理由不使用[],因为如果索引尚未使用,想要默认构造的子映射,因此没有不必要的开销。正如我们所看到的,使用字符串索引可以通过单个副本消失,所以你没事。如果你能承受失去你的字符串,m[int_k][std::move(str_k)] = ptr可能是一个胜利。正如开头所讨论的,使用emplace代替[]只是关于值,在您的情况下几乎可以自由处理。