这实际上是一个格式正确的程序,具有两个同样有效的执行路径,因此两个编译器都是正确的。
a[1] = a.size()
在此表达式中,对1的两个操作数的求值是无序列的。
§1.9/ 15 [intro.execution]除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的求值是无序列的。
但是,函数调用不会交错,因此对1和size + 1的调用实际上是不确定地排序的,而不是未排序的。
§1.9/ 15 [intro.execution]在执行被调用函数的主体之前或之后,未按其他方式专门对调用函数中的每个评估(包括其他函数调用)进行不确定性排序 功能。
这意味着函数调用可能以以下两个顺序之一发生:
1然后size + 1
1 then size + 1
如果某个键不存在,而您使用该键调用1,它将被添加到地图中,从而更改地图的大小。 因此,在第一种情况下,将添加密钥,将检索大小(现在为1),并将size + 1分配给该密钥。 在第二种情况下,将检索大小(为0),添加密钥,并将0分配给该密钥。
注意,这不是导致不确定行为的情况。 当两个修改或一个修改和同一标量对象的读取未排序时,将发生未定义的行为。
§1.9/ 15 [intro.executionion]如果相对于同一标量对象上的另一边效果或使用同一标量对象的值进行的值计算,相对于同一标量对象上的另一面效果未排序,则该行为未定义。
在这种情况下,它们并不是无序的,而是不确定地排序。
因此,我们要做的是执行程序的两个同等有效的顺序。 任何一种都可能发生,并且两者都给出有效的输出。 这是未指定的行为。
§1.3.25[defns.unspecified]
未指明的行为
行为,对于一个格式正确的程序构造和正确的数据,取决于实现
因此,回答您的问题:
哪个编译器正确?
他们俩都是。
难道我做错了什么?
大概。 您不太可能希望编写具有两个这样的执行路径的代码。 与未定义的行为不同,未指定的行为可能还可以,因为可以将其解析为单个可观察的输出,但是如果可以避免的话,这首先是不值得的。 相反,请勿编写具有这种歧义的代码。 根据您希望正确的路径是什么,可以执行以下任一操作:
auto size = a.size();
a[1] = size; // value is 0
要么:
a[1];
a[1] = a.size(); // value is 1
如果您希望结果为1,并且知道该键尚不存在,则当然可以执行第一个代码,但要分配size + 1。