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

Nic*_*ter 12 c++ map

假设我有一个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).值得一读.


Mat*_*lia 7

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

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

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


M.M*_*M.M 5

是的,这是好的,新条目的默认价值value_type()0int

它仍然如此+=,但0 += 1给出了1.