Является ли эта операция правильно упорядоченной?

В следующем фрагменте кода из более обширного фрагмента кода, представленного

void func(int* usedNum, int wher) {
    *usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1; 
}

int main(void) {
   int a = 11, b = 2; 
   func(&a, b); 
}

предупреждение издается

 warning: operation on '* usedNum' may be undefined [-Wsequence-point]
 *usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1; 

Есть ли проблема с кодом?

Мой источник сомнений был этот и часть, где говорится

Точки последовательности в логических выражениях, таких как && и || и тернарный оператор?:, а оператор запятой означает, что операнд левой стороны оценивается перед операндом правой стороны. Эти несколько операндов являются единственными операндами в С++, которые вводят точки последовательности.

TL;DR

Для тех, кто находит пытки, чтобы читать комментарии: первоначальный вопрос не был правильно поставлен, и было бы несправедливо создавать неправильные представления. Мой взгляд на эту тему имел две стороны

  • Тернарный оператор не испортит (неожиданным образом) точки последовательности (что выполняется, две ветки секвенированы в каждой версии C, С++ - см. предоставленную ссылку)

  • Является ли x = ++x проблемой? Как видно из ссылки coliru, мы компилируем для С++ 14. Там операция хорошо определена (ссылки на комментарии), но более старые версии С++ и c рассматривают это как undefined. Так почему же существует предупреждение?

Ответы фокусируются как на C, так и на С++; это - хорошая ссылка. Наконец, тег C был первоначально (мой плохой) и не может быть удален, потому что существующие вышеперечисленные ответы относятся к нему

Ответы

Ответ 1

Когда условие истинно, это эквивалентно выражению x = ++x. В C и версиях С++ до С++ 11 это представляет собой модификацию и чтение x без промежуточной точки последовательности и, следовательно, поведение undefined, если соблюдается истинная ветвь. Начиная с С++ 11, x = ++x упорядочен и четко определен.


Изменить. Чтобы прояснить некоторые проблемы из комментариев.

1) это будет хорошо определено во всех стандартах C и С++:

x = (++x, x); // RHS evaluates to x after increment

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

2) Тернарный оператор вводит точку последовательности

Это точка последовательности между условием и двумя ветвями. Но это не указывает точку последовательности между веткой и присваиванием.

Ответ 2

Предупреждение, которое вы получаете, вероятно, связано с тем, что вы компилируете свой код в режиме С++ 03 или старше. В C99 и С++ 03 выражение

x = ++x;

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

Это правило изменяется в C11 и С++ 11. Согласно C11, правило выглядит следующим образом:

C11: 6.5 Выражения:

Если побочный эффект на скалярном объекте непересекающийся по отношению к другому побочному эффекту на том же скалярном объекте или вычислении значения с использованием значения одного и того же скалярного объекта, поведение undefined.

Когда *usedNum + 1 > wher будет true, тогда

*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;   

будет эквивалентно

*usedNum = ++(*usedNum);  

и согласно новому правилу это хорошо определено в С++ 11, это связано с тем, что побочный эффект pre ++ секвенирован перед побочным эффектом оператором =. Подробнее читайте этот ответ.

Но одно и то же выражение *usedNum = ++(*usedNum); вызывает undefined поведение в C11. Причина в том, что нет гарантии того, что побочный эффект оператора = секвенируется после побочного эффекта оператора pre ++.


Примечание: В выражении

a = x++ ? x++ : 0; 

есть точка последовательности после первого x++, и, следовательно, поведение хорошо определено. То же самое верно для

x = (++x, x);  

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