После ответа этот вопрос, было долгое обсуждение того, является ли рассматриваемый код неопределенным поведением или нет. Вот код:
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"]
не существует, утверждалось, что он будет назначен дважды без последовательности между ними. Лично я не видел, как это могло бы произойти, если бы две вещи, которые я считал правдой, были:
Когда выбирается сторона, подлежащая оценке, вся сторона должна быть оценена, прежде чем другая сторона может начать.
Такие конструкции, как word_count [«a»] = 1, демонстрируют четко определенное поведение даже в том случае, когда элемент вставляется и затем назначается.
Верны ли эти два утверждения? В конечном счете, это действительно неопределенное поведение, и если это так, то почему второе утверждение работает (при условии, что оно работает)? Если второе неверно, я считаю, что все myMap[i]++;
в мире было бы плохо сформировано.
Полезная ссылка: Неопределенные точки поведения и последовательности
Поведение не определено, но не определено.
Обратите внимание, что в выражении:
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"
нет на карте.
Тем не менее, выражение будет иметь другой результат, в зависимости от того, какая сторона назначения оценивается первой. Таким образом, поведение не определено, но не определено.
Это не указано, но не определено.
word_count.operator[]("a")
а также word_count.count("a")
являются функциональными вызовами. Стандартное выполнение функций гарантировано не перемежается — либо первое полностью секвенируется перед вторым, либо наоборот.
Конкретное определение может варьироваться в зависимости от стандарта, в C ++ 11 соответствующий пункт в 1.9 / 15:
Каждая оценка в вызывающей функции (включая другую функцию
звонки), который не определен каким-либо иным образом до или после
выполнение тела вызываемой функции неопределенно
последовательный относительно выполнения вызываемой функции.99) Другими словами, выполнение функций не чередуется друг с другом.
неопределенно последовательность определяется в 1.9 / 13:
Оценки A и B неопределенно упорядочены, когда любой из
последовательность перед B или B последовательность перед A, но она не указана
который.
Например, оценка:
word_count["a"] = word_count.count("a");
состоит из трех частей:
word_count.operator[]("a")
word_count.count("a")
Позволять <
означает «секвенируется раньше». Цитируемая часть стандарта гарантирует, что либо 1 < 2
или же 2 < 1
, Часть, приведенная в ответе @Andy Prowl, также показывает, что оба 1 < 3
а также 2 < 3
, Итак, есть только два варианта:
1 < 2 < 3
2 < 1 < 3
В обоих случаях все полностью упорядочено, и у UB нет шансов.