安全使用+ =运算符使用[]创建一个新的std :: map条目?

时间:2014-04-28 05:39:02

标签: c++ map

我们说我有一个std::map<int, int>,这样做会安全吗?

std::map<int, int> m_map;
m_map[0] += 1;

如果我执行此操作时地图中的密钥0不存在,那么它如何知道要将1添加到哪个值?

我希望std :: map通过在值在地图中创建新条目的情况下执行=而不是+=来处理此问题。这样我就不必这样做了:

std::map<int, int>::iterator it = m_map.find(0);
if(it != m_map.end()) {
    it->second += 1;
}
else {
    m_map[0] = 1;
}

4 个答案:

答案 0 :(得分:18)

由于之前未映射的密钥,在operator[]调用时插入到地图中的元素为 值初始化 。如果您没有看到该语法,请考虑以下代码段中 () 的特殊含义。 parens很重要。它们在初始化树中引入了与 default-initialization 不同的行程。两者都很重要;两者都按语言标准列出。

int i = int();

事实证明,标量(包括指针)的值初始化最终会屈服于 零初始化 。虽然很奇怪,但先前的片段值初始化int的实例,由于int是标量,因此它将变为零初始化,然后将其复制到i。 (公平地说,几乎肯定会有一些消失,但基本面也是如此)。

无论如何,由于该功能,您可以放心:

m_map[0] += 1;

甚至是这样:

++m_map[0];

如果之前未映射索引,则会添加一个值初始化元素,这将为标量零初始化,这意味着您将正式开始使用

值得一提的是,对于带有隐式声明的构造函数的任何类型的,都会发生类似的活动。无论是否微不足道,都会发生一些有趣的事情。

struct S { int a; int b; };
std::map<int, S> mymap;

++mymap[0].a;

在执行上述操作后,a成员是否可靠地0在我们的容器中映射到1,确实如此。此外,请考虑这一点:

struct S { int a; std::string str; };
std::map<int, S> mymap;

++mymap[0].a;

现在S有一个非平凡的隐式构造函数(它必须,因为它必须构造str)。但是a成员映射到我们的容器中的0仍然可靠地零初始化(因此1在上面的行之后)? ,确实如此。

如果对引用的不同初始化路径感到好奇,see this question and answer。或者回顾一下C ++ 11标准,特别是 C ++11§8.5Initializers,(p5,p7,p10)。值得一读。

答案 1 :(得分:7)

即使使用较短的代码段,您也能保证获得1分。

关键是operator[]必须在返回对它的引用之前在地图中创建元素,所以当你到达+=时元素已经存在,如果必须现在创建,则值为零(因为地图的元素是值初始化的)。

(顺便说一句,这就是为什么当你使用类std::map作为值时,如果你想使用operator[],它必须有一个默认的构造函数,即使你指定了一个对象立即)

答案 2 :(得分:4)

是的,没关系,新条目默认为value_type()0int

它仍然+=,但0 += 11

答案 3 :(得分:2)

Map使用默认构造函数初始化。(所有int为零)

因此地图中不存在0,它会被初始化为包含值0,而+= 1之后会添加1。

所以你可以毫无后顾之忧地使用更高版本的代码。

cplusplus.com

引用调用map_obj [k] 的效果

  

如果k匹配容器中元素的键,则为函数   返回对其映射值的引用。

     

如果k与容器中任何元素的键不匹配,则   function使用该键插入一个新元素并返回一个引用   到它的映射值。请注意,这总是会增加容器   即使没有为元素分配映射值,也将大小加1   element是使用其默认构造函数构造的。