Ответ 1
Это действительно хорошо сформированная программа, которая имеет два одинаково допустимых пути выполнения, поэтому оба компилятора правы.
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
будет назначен этому ключу.
Обратите внимание, что это не та ситуация, которая вызывает поведение undefined. undefined поведение происходит, когда две модификации или модификация и чтение одного и того же скалярного объекта не подвержены.
§1.9/15 [intro.execution] Если побочный эффект скалярного объекта не влияет на какой-либо другой побочный эффект на одном и том же скалярном объекте или вычисление значения с использованием значения одного и того же скалярного объекта, поведение не определено.
В этой ситуации они не являются необъективными, но неопределенно упорядочены.
Итак, у нас есть два одинаково допустимых порядка выполнения программы. Либо это может произойти, и оба дают достоверный результат. Это неуказанное поведение.
§1.3.25 [defns.unspecified]
неспецифицированное поведение
поведение, для хорошо сформированной конструкции программы и правильных данных, которая зависит от реализации
Итак, чтобы ответить на ваши вопросы:
Какой компилятор прав?
Оба они.
Я что-то делаю неправильно?
Возможно. Маловероятно, что вы захотите написать код, который имеет два пути выполнения, подобные этому. Неопределенное поведение может быть в порядке, в отличие от поведения undefined, потому что оно может быть разрешено для одного наблюдаемого вывода, но в первую очередь это не стоит, если вы можете его избежать. Вместо этого не пишите код, который имеет такую двусмысленность. В зависимости от того, что именно вы хотите установить правильный путь, вы можете выполнить одно из следующих действий:
auto size = a.size();
a[1] = size; // value is 0
Или:
a[1];
a[1] = a.size(); // value is 1
Если вы хотите, чтобы результат был 1
, и вы знаете, что ключ еще не существует, вы, конечно, можете сделать первый код, но назначьте size + 1
.