Безопасно ли помещать операторы инкремента / декремента в тройные / условные операторы?

Вот пример

#include <iostream>
using namespace std;
int main()
{
int x = 0;
cout << (x == 0 ? x++ : x) << endl; //operator in branch
cout << "x=" << x << endl;
cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition
cout << "x=" << x << endl;
return 0;
}

выход:

0
x=1
1
x=1

Я понимаю вывод, но это неопределенное поведение или нет? Гарантирован ли порядок оценки в любом случае?

Даже если это гарантировано, я прекрасно понимаю, что увеличение / уменьшение может быстро стать проблемой для удобства чтения. Я только спрашиваю, поскольку я видел подобный код и был сразу же не уверен, учитывая, что есть много примеров неоднозначного / неопределенного использования операторов увеличения / уменьшения, таких как …

  • C ++ не определяет порядок, в котором оцениваются параметры функции.

    int nValue = Add(x, ++x);
    
  • Язык C ++ говорит, что вы не можете изменять переменную более одного раза между точками последовательности.

     x = ++y + y++
    
  • Поскольку операторы увеличения и уменьшения имеют побочные эффекты, использование выражений с операторами увеличения или уменьшения в макросе препроцессора может привести к нежелательным результатам.

     #define max(a,b) ((a)<(b))?(b):(a)
    k = max( ++i, j );
    

26

Решение

Для условного оператора (§5.16 [expr.cond] / p1):

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

Для логического оператора ИЛИ (§5.15 [expr.log.or] / p1-2):

второй операнд не оценивается, если первый операнд оценивается как true,
[…] Если вычисляется второе выражение, каждое вычисление значения и
побочный эффект, связанный с первым выражением
каждое значение вычисления и побочный эффект, связанный со вторым
выражение.

Поведение вашего кода четко определено.

34

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

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

Один за раз

 cout << (x == 0 ? x++ : x) << endl; //operator in branch

Будет всегда выводить x но будет увеличивать его, только если это было 0.

 cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition

Это тоже хорошо определено, если x было 1, оно не будет оценивать RHS, если не было, то уменьшит его, но --x никогда не будет 0, поэтому оно будет истинным, если x == 1, и в этом случае x также теперь будет 0.

В последнем случае, если х INT_MIN это не вполне определенное поведение, чтобы уменьшать его (и он будет выполняться).

Это не может произойти в первом случае, когда х не будет 0, если это INT_MAX так что вы в безопасности.

10

Я понимаю вывод, но это неопределенное поведение или нет?

Код отлично определен. Стандарт C11 гласит:

6.5.15 Условный оператор

Первый операнд оценивается; между его оценкой и
оценка второго или третьего операнда
(что бы ни оценивалось). Второй операнд
оценивается, только если первое сравнение не равно 0; третий операнд вычисляется только если
первое сравнение равно 0; результатом является значение второго или третьего операнда
(в зависимости от того, что оценивается), преобразуется в тип, описанный ниже. 110)

6.5.14 Логический оператор ИЛИ

В отличие от побитового | оператор, || оператор гарантирует оценку слева направо; если
второй операнд оценивается, между оценками первого
и вторые операнды
. Если первый операнд сравнивается неравно 0второй операнд
Не Оценено.

В дальнейшем вики объясняет это примером:

  • Между оценкой левого и правого операндов && (логическое И), || (логическое ИЛИ) (как часть оценки короткого замыкания), и comma operators, Например, в выражении *p++ != 0 && *q++ != 0, все побочные эффекты под-выражения *p++ != 0 завершены до любой попытки доступа q,

  • Между оценкой первого операнда троичного оператора «вопросительный знак» и второго или третьего операнда. Например, в выражении a = (*p++) ? (*p++) : 0 после первого есть точка последовательности *p++Это означает, что он уже был увеличен ко времени выполнения второго экземпляра.

Правило для || а также ?: то же самое для C ++ (раздел 5.15 и 5.16) как в C.


Гарантирован ли порядок оценки в любом случае?

Да. Порядок оценки операндов операторов ||, &&, , а также ?: гарантированно будет слева направо.

7

В C сохраненное значение объекта может быть изменено только один раз между двумя точками последовательности.

Точка последовательности происходит:

  1. В конце полного выражения.
  2. На &&, || а также ?: операторы
  3. При вызове функции.

Так, например, это выражение x = i++ * i++ является не определено, в то время как x = i++ && i++ является совершенно законно.

Ваш код показывает определенное поведение.

int x = 0;

соиЬ << (х == 0? х ++: х) << епсИ;

В приведенном выше выражении x является 0, так x++ будет выполнено, здесь x ++ — постинкремент, поэтому он выведет 0,

соиЬ << «Х =» << Икс << епсИ;

В приведенном выше выражении как x теперь имеет значение 1 поэтому вывод будет 1,

соиЬ << (x == 1 || —x == 0? 1: 2) << епсИ;

Вот x является 1 поэтому следующее условие не оценивается (--x == 0) и на выходе будет 1,

соиЬ << «Х =» << Икс << епсИ;

Как выражение --x == 0 не оценивается, выход снова будет 1,

3

Да, безопасно использовать операторы увеличения / уменьшения, как у вас. Вот что происходит в вашем коде:

Фрагмент №1

cout << (x == 0 ? x++ : x) << endl; //operator in branch

В этом фрагменте вы тестируете x == 0, который true, Так как это trueВаше троичное выражение оценивает x++,
Так как вы используете пост-приращение здесь, исходное значение для x выводится на стандартный поток вывода, а потом x увеличивается

Фрагмент № 2

cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition

Этот фрагмент немного более запутанный, но все же дает предсказуемый результат. С этой точки зрения, x = 1 из первого фрагмента. В троичном выражении условная часть вычисляется первой; однако из-за Короткое замыкание, второе условие, --x == 0, никогда не оценивается.

Для C ++ операторы || а также && являются короткозамкнутыми логическими операторами для логическое ИЛИ а также логическое И соответственно. Когда вы используете эти операторы, ваши условия проверяются (слева направо) до определения окончательного результата. Как только результат определен, больше никаких условий не проверяется.

Глядя на фрагмент # 2, ваше первое условие проверяет, x == 1, Поскольку ваше первое условие оценивается как true и вы используете логическое ИЛИ, нет необходимости продолжать оценивать другие условия. Это означает, что --x == 0 является никогда не казнил.


Краткое примечание о коротком замыкании:

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

if (task1() && task2())
{
//...Do something...
}

В этом примере task2 никогда не должен называться, если task1 завершается успешно (task2 зависит от некоторых данных, которые изменяются task1).

Потому что мы используем оператор короткого замыкания И, если task1 не возвращается false, тогда оператор if обладает достаточной информацией, чтобы досрочно завершить работу и прекратить проверку других условий. Это означает, что task2 никогда не называется.

2
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector