我只是坚持以下问题:这是否会导致未定义的行为?为什么?
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;
答案 0 :(得分:3)
这没有什么不确定的。 operator[]
返回对映射条目的左值引用(如果需要,它将创建它)。那么你只是递增这个左值表达式,即基础条目。
评估顺序的规则规定,对于修改分配操作,在评估左边(即对地图条目的左值引用)和右边(即常量1
)之后,严格对副作用进行排序。操作数。在这个例子中没有任何含糊之处。
更新:在您更新的示例中,没有任何更改。同样,修改m[10]
的副作用在其他操作之后严格排序(即在右侧评估为左值,在右侧评估,并执行添加)。
相关的排序规则,来自cppreference:
8)内置的副作用(左参数的修改) 赋值运算符和所有内置复合赋值运算符 在计算值(但不是副作用)之后进行排序 左右参数,并在值之前排序 计算赋值表达式(即返回之前) 对修改对象的引用)
答案 1 :(得分:0)
我不太确定你的担心是什么(也许你应该澄清你的问题,如果答案不充分),但m[10] += 1;
没有被翻译为m[10] = m[10] + 1;
因为{{1}用户定义的类类型,重载的运算符永远不会被编译器翻译。对于具有用户定义的类类型的m
和a
个对象:
b
并不代表a+=b
(除非您这样做)a = a + b
并不代表a!=b
(除非您这样做)此外,函数调用永远不会重复。
所以!(a==b)
表示调用重载m[10] += 1;
一次; return类型是一个引用,所以表达式是一个左值;然后将内置运算符operator[]
应用于左值。
没有评估问题的顺序。甚至没有多个可能的评估订单!
此外,您需要记住+=
的行为不像std::map<>::operator[]
(或std::vector<>::operator[]
),因为std::deque
是完全不同的抽象: map
和vector
是Sequence概念的实现(位置很重要),但deque
是一个关联容器(其中“key”很重要,而不是位置):
map
采用数字索引,如果这样的索引不引用向量的元素,则没有意义。std::vector<>::operator[]
获取一个键(可以是满足基本约束的任何类型),将创建一个(键,值)对(如果不存在)。请注意,出于这个原因, std::map<>::operator[]
本质上是一个修改操作,因此是非const ,而std::map<>::operator[]
本身并不是修改,但可以允许通过返回的引用进行修改,因此是“传递”const:如果std::vector<>::operator[]
是非常量向量,则v[i]
将是可修改的左值,如果v
是常量向量,则为{1}。
所以不用担心,代码具有完美定义的行为。