std :: map operator [] - 未定义的行为?

时间:2016-06-03 12:58:26

标签: c++ dictionary stl undefined-behavior stdmap

我只是坚持以下问题:这是否会导致未定义的行为?为什么?

std::map<int, int> m;
m[10] += 1;

它编译并运行完美,但它没有任何证据。 它类似于一个常见的UB示例i = ++i + i++;,因为operator[]确实有副作用,但另一方面假设任何评估顺序(从左到右和从右到左)将我带到地图的相同最终状态

P.S。可能相关:http://en.cppreference.com/w/cpp/language/eval_order

修改

对不起,我应该写的

 m[10] = m[10] + 1;

2 个答案:

答案 0 :(得分:3)

这没有什么不确定的。 operator[]返回对映射条目的左值引用(如果需要,它将创建它)。那么你只是递增这个左值表达式,即基础条目。

评估顺序的规则规定,对于修改分配操作,在评估左边(即对地图条目的左值引用)和右边(即常量1)之后,严格对副作用进行排序。操作数。在这个例子中没有任何含糊之处。

更新:在您更新的示例中,没有任何更改。同样,修改m[10]的副作用在其他操作之后严格排序(即在右侧评估为左值,在右侧评估,并执行添加)。

相关的排序规则,来自cppreference

  

8)内置的副作用(左参数的修改)   赋值运算符和所有内置复合赋值运算符   在计算值(但不是副作用)之后进行排序   左右参数,并在值之前排序   计算赋值表达式(即返回之前)   对修改对象的引用)

答案 1 :(得分:0)

我不太确定你的担心是什么(也许你应该澄清你的问题,如果答案不充分),但m[10] += 1;没有被翻译为m[10] = m[10] + 1;因为{{1}用户定义的类类型,重载的运算符永远不会被编译器翻译。对于具有用户定义的类类型的ma个对象:

  • b并不代表a+=b(除非您这样做)
  • a = a + b并不代表a!=b(除非您这样做)

此外,函数调用永远不会重复。

所以!(a==b)表示调用重载m[10] += 1;一次; return类型是一个引用,所以表达式是一个左值;然后将内置运算符operator[]应用于左值。

没有评估问题的顺序。甚至没有多个可能的评估订单!

此外,您需要记住+=的行为不像std::map<>::operator[](或std::vector<>::operator[]),因为std::deque是完全不同的抽象: mapvector是Sequence概念的实现(位置很重要),但deque是一个关联容器(其中“key”很重要,而不是位置):

  • map采用数字索引,如果这样的索引不引用向量的元素,则没有意义。
  • std::vector<>::operator[]获取一个键(可以是满足基本约束的任何类型),将创建一个(键,值)对(如果不存在)

请注意,出于这个原因, std::map<>::operator[]本质上是一个修改操作,因此是非const ,而std::map<>::operator[]本身并不是修改,但可以允许通过返回的引用进行修改,因此是“传递”const:如果std::vector<>::operator[]是非常量向量,则v[i]将是可修改的左值,如果v是常量向量,则为{1}。

所以不用担心,代码具有完美定义的行为。