Индексирует ли новый элемент карты и имеет ли что-то, что читает его, присвоенное ему неопределенное поведение, или просто не указано?

После ответа этот вопрос, было долгое обсуждение того, является ли рассматриваемый код неопределенным поведением или нет. Вот код:

std::map<string, size_t> word_count;
word_count["a"] = word_count.count("a") == 0 ? 1 : 2;

Прежде всего, было установлено, что это было по крайней мере не определено. Результат отличается в зависимости от того, какая сторона задания оценивается первой. В своем ответе я проследил каждый из четырех полученных случаев, указав факторы, какая сторона оценивается первой и существует ли элемент до этого.

Была также короткая форма:

(x = 0) = (x == 0) ? 1 : 2; //started as
(x = 0) = (y == "a") ? 1 : 2; //changed to

Я утверждал, что это было больше похоже на это:

(x = 0, x) = (x == 0) ? 1 : 2; //comma sequences x, like [] should

В конце концов, я нашел пример, который, кажется, работал для меня:

i = (++i,i++,i); //well-defined per SO:Undefined Behaviour and Sequence Points

Возвращаясь к оригиналу, я разбил его на соответствующие вызовы функций, чтобы было легче следовать:

operator=(word_count.operator[]("a"), word_count.count("a") == 0 ? 1 : 2);
^       inserts element^                        ^reads same element
|
assigns to element

Если word_count["a"] не существует, утверждалось, что он будет назначен дважды без последовательности между ними. Лично я не видел, как это могло бы произойти, если бы две вещи, которые я считал правдой, были:

  1. Когда выбирается сторона, подлежащая оценке, вся сторона должна быть оценена, прежде чем другая сторона может начать.

  2. Такие конструкции, как word_count [«a»] = 1, демонстрируют четко определенное поведение даже в том случае, когда элемент вставляется и затем назначается.

Верны ли эти два утверждения? В конечном счете, это действительно неопределенное поведение, и если это так, то почему второе утверждение работает (при условии, что оно работает)? Если второе неверно, я считаю, что все myMap[i]++;в мире было бы плохо сформировано.

Полезная ссылка: Неопределенные точки поведения и последовательности

4

Решение

Поведение не определено, но не определено.

Обратите внимание, что в выражении:

word_count["a"] = word_count.count("a") == 0 ? 1 : 2;
//              ^

Оператор присваивания отмечен ^ это встроенный оператор присваивания, потому что std::map«s operator [] возвращает size_t&,

В соответствии с пунктом 5.17 / 1 стандарта C ++ 11 для встроенных операторов присваивания:

Оператор присваивания (=) и составные операторы присваивания все группы справа налево. [..] Во всех случаях присвоение упорядочивается после значения
вычисление правого и левого операндов, а также до вычисления значения выражения присваивания
.
Что касается вызова функции с неопределенной последовательностью, операция составного присваивания
единая оценка.

Это означает, что во встроенном назначении, таком как:

a = b

Сначала оцениваются операнды (в неуказанном порядке), затем выполняется присваивание и, наконец, выполняется вычисление значения всего выражения присваивания.

Учитывая оригинальное выражение:

word_count["a"] = word_count.count("a") == 0 ? 1 : 2;
//              ^

Из-за приведенного выше абзаца ни в коем случае не существует двух непоследовательных присвоений одному и тому же объекту: назначение помечено ^ всегда будет последовательность после задание выполняется operator [] (как часть оценки левого выражения) в случае, если ключ "a" нет на карте.

Тем не менее, выражение будет иметь другой результат, в зависимости от того, какая сторона назначения оценивается первой. Таким образом, поведение не определено, но не определено.

5

Другие решения

Это не указано, но не определено.

word_count.operator[]("a") а также word_count.count("a") являются функциональными вызовами. Стандартное выполнение функций гарантировано не перемежается — либо первое полностью секвенируется перед вторым, либо наоборот.

Конкретное определение может варьироваться в зависимости от стандарта, в C ++ 11 соответствующий пункт в 1.9 / 15:

Каждая оценка в вызывающей функции (включая другую функцию
звонки), который не определен каким-либо иным образом до или после
выполнение тела вызываемой функции неопределенно
последовательный
относительно выполнения вызываемой функции.9

9) Другими словами, выполнение функций не чередуется друг с другом.

неопределенно последовательность определяется в 1.9 / 13:

Оценки A и B неопределенно упорядочены, когда любой из
последовательность перед B или B последовательность перед A, но она не указана
который.

Например, оценка:

word_count["a"] = word_count.count("a");

состоит из трех частей:

  1. исполнение word_count.operator[]("a")
  2. выполнение word_count.count("a")
  3. назначение

Позволять < означает «секвенируется раньше». Цитируемая часть стандарта гарантирует, что либо 1 < 2 или же 2 < 1, Часть, приведенная в ответе @Andy Prowl, также показывает, что оба 1 < 3 а также 2 < 3, Итак, есть только два варианта:

  • 1 < 2 < 3
  • 2 < 1 < 3

В обоих случаях все полностью упорядочено, и у UB нет шансов.

2

По вопросам рекламы [email protected]