我们说我有一个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;
}
答案 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()
,0
为int
。
它仍然+=
,但0 += 1
给1
。
答案 3 :(得分:2)
Map使用默认构造函数初始化。(所有int为零)
因此地图中不存在0
,它会被初始化为包含值0,而+= 1
之后会添加1。
所以你可以毫无后顾之忧地使用更高版本的代码。
引用调用map_obj [k] 的效果如果k匹配容器中元素的键,则为函数 返回对其映射值的引用。
如果k与容器中任何元素的键不匹配,则 function使用该键插入一个新元素并返回一个引用 到它的映射值。请注意,这总是会增加容器 即使没有为元素分配映射值,也将大小加1 element是使用其默认构造函数构造的。