假设我有一个std::map<int, int>,这样做是否安全?
std::map<int, int> m_map;
m_map[0] += 1;
Run Code Online (Sandbox Code Playgroud)
如果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;
}
Run Code Online (Sandbox Code Playgroud)
Who*_*aig 18
operator[]由于先前未映射的键而在调用时插入到映射中的元素是值初始化的.如果您没有看到该语法,请考虑()以下代码段中的特殊含义.parens很重要.它们在初始化树中引入了与默认初始化不同的行程.两者都很重要; 两者都按语言标准列出.
int i = int();
Run Code Online (Sandbox Code Playgroud)
事实证明,标量(包括指针)的值初始化最终会屈服于零初始化.虽然很奇怪,但先前的片段值初始化了一个实例int,由于int是标量而变为零初始化,然后将其复制到i.(公平地说,几乎肯定会有一些消失,但基本面也是如此).
无论如何,由于该功能,您可以放心:
m_map[0] += 1;
Run Code Online (Sandbox Code Playgroud)
甚至这个:
++m_map[0];
Run Code Online (Sandbox Code Playgroud)
如果索引未事先映射,则会添加一个值初始化元素,这将为标量零初始化,这意味着您将正式开始为零.
值得一提的是,对于具有隐式声明的构造函数的任何类型,都会发生类似的活动.无论是否琐碎,都会发生一些有趣的事情.
struct S { int a; int b; };
std::map<int, S> mymap;
++mymap[0].a;
Run Code Online (Sandbox Code Playgroud)
在执行上述操作后,a成员是否0可靠地映射到我们的容器中1?是的,确实如此.此外,考虑一下:
struct S { int a; std::string str; };
std::map<int, S> mymap;
++mymap[0].a;
Run Code Online (Sandbox Code Playgroud)
现在S有一个非平凡的隐式构造函数(它必须,因为它必须构造str).但是a映射到0我们容器中的成员是否仍然可靠地零初始化(因此1在上面的行之后)?是的,确实如此.
如果对引用的不同初始化路径感到好奇,请参阅此问题和答案.或者回顾一下C++ 11标准,特别是C++11§8.5Initializers,(p5,p7,p10).值得一读.
保证即使使用较短的代码段也能获得1分.
关键是operator[]必须在返回对它的引用之前在地图中创建元素,所以当你到达+=元素已经存在时,如果必须现在创建它,则值为零(因为地图的元素)是价值初始化的).
(顺便说一句,这就是为什么当你使用std::map类类型作为值时,如果你想使用它,它必须有一个默认的构造函数operator[],即使你立即为它分配一个对象)