Да, ваш первый пример имеет неопределенное поведение, потому что у нас есть неупорядоченная модификация и доступ к ячейке памяти, что является неопределенным поведением. Это описано в черновом стандарте C ++ [intro.execution] p10 ( выделение шахты ):
За исключением отмеченных случаев, оценки операндов отдельных операторов
и из подвыражений отдельных выражений нет последовательности .
[Примечание: в выражении, которое оценивается более одного раза в течение
выполнение программы, непоследовательное и неопределенно упорядоченное
оценки его подвыражений не должны выполняться последовательно
в разных оценках. - примечание конца] Расчет стоимости
операнды оператора секвенируются до вычисления значения
результат оператора. Если побочный эффект в ячейке памяти
([intro.memory]) не секвенируется относительно любого другого побочного эффекта
в той же ячейке памяти или вычислении значения с использованием значения
любой объект в той же области памяти, и они не являются потенциально
одновременно ([intro.multithread]), поведение не определено . [ Заметка:
Следующий подпункт налагает аналогичные, но более сложные ограничения на
потенциально параллельные вычисления. - примечание конца] [Пример:
void g(int i) {
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined
i = i + 1; // the value of i is incremented
}
- конец примера]
Если мы проверим раздел реляционных операторов, который охватывает <=
[expr.rel] , он не определяет порядок вычисления, поэтому мы покрыты intro.exection
и, таким образом, мы не определили поведение.
Наличие неопределенного порядка вычисления недостаточно для неопределенного поведения, как показано в примере в Порядок вычисления оператора присваивания в C ++ .
Ваш второй пример позволяет избежать неопределенного поведения, поскольку вы не вводите побочный эффект для ival
, вы просто читаете ячейку памяти дважды.
Я считаю, что это разумный способ решения проблемы, он читабелен и не удивителен. Альтернативный способ может включать введение второй переменной, например, index
и prev_index
. Трудно прийти с быстрым правилом, учитывая такой небольшой фрагмент кода.