这实际上是一个形式良好的程序,有两个等效的执行路径,所以两个编译器都是正确的。
a[1] = a.size()
在这个表达式中,
=
的两个操作数的评估是无序的。
§1.9/15 [intro.execution] 除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值都是未排序的。
但是,函数调用不会交错,因此对 operator[]
和 size
的调用实际上是 不确定有序,而不是无序的。
§1.9/15 [intro.execution] 在调用函数的函数中(包括其他函数调用)中没有被明确排序在调用函数体执行之前或之后的每个评估与调用函数的执行不确定排序。
这意味着函数调用可能以以下两种顺序之一发生:
operator[]
然后是 size
size
然后是 operator[]
如果一个键不存在,并且您使用该键调用 operator[]
,它将添加到映射中,从而改变映射的大小。 因此,在第一种情况下,将添加键,检索大小(现在为1),并将 1
赋值给该键。 在第二种情况下,将检索大小(为0),添加键,并将 0
赋值给该键。
请注意,这不是导致未定义行为的情况。 当两个修改或一个标量对象的修改和读取是无序的时,会发生未定义行为。
§1.9/15 [intro.execution] 如果与同一标量对象的另一个副作用或使用同一标量对象的值进行值计算的副作用无序,则行为是未定义的。
在这种情况下,它们不是无序的,而是不确定有序的。
因此,我们拥有程序执行的两个等效有效顺序。 任何一个都可能发生并且都会产生有效输出。 这是未指定的行为。
§1.3.25 [defns.unspecified]
未指定的行为
对于正确的数据和格式良好的程序构造而言,行为取决于实现
所以回答你的问题:
哪个编译器是正确的?
它们两个都是正确的。
我做错了什么吗?
可能。 您不太可能想编写具有这两种执行路径的代码。 未指定的行为可能是可以接受的,与未定义的行为不同,因为它可以解析为单个可观察的输出,但如果可以避免,则在第一次就没有必要拥有此类模棱两可的行为。 相反,不要编写具有这种模棱两可性质的代码。 根据您想要的正确路径,您可以执行以下操作之一:
auto size = a.size()
a[1] = size
或者:
a[1];
a[1] = a.size(); // value is 1
如果你想要结果为1
,并且你知道这个键还不存在,当然可以使用第一段代码但是将size + 1
赋值。