Обязательно ли сокращение логических операторов?И порядок оценки?

StackOverflow https://stackoverflow.com/questions/628526

Вопрос

Соответствует ли стандарт ANSI мандат логические операторы, которые нужно замкнуть, в C или C++?

Я в замешательстве, поскольку помню книгу K&R, в которой говорилось, что ваш код не должен зависеть от короткого замыкания этих операций, а они могут и не быть.Может ли кто-нибудь указать, где в стандарте говорится, что логические операции всегда закорочены?Меня больше всего интересует C++, было бы здорово ответить и на C.

Я также помню, как читал (не помню где), что порядок вычислений строго не определен, поэтому ваш код не должен зависеть или предполагать, что функции внутри выражения будут выполняться в определенном порядке:к концу инструкции все указанные функции будут вызваны, но компилятор имеет свободу выбора наиболее эффективного порядка.

Указывает ли стандарт порядок вычисления этого выражения?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";
Это было полезно?

Решение

Да, для операторов требуется короткое замыкание и порядок оценки. || и && как в стандартах C, так и в C++.

Стандарт C++ гласит (в стандарте C должно быть эквивалентное предложение):

1.9.18

При оценке следующих выражений

a && b
a || b
a ? b : c
a , b

используя встроенное значение операторов в этих выражениях, существует точка последовательности после вычисления первого выражения (12).

В C++ есть дополнительная ловушка:короткое замыкание делает НЕТ применять к типам, которые перегружают операторы || и &&.

Сноска 12:Операторы, указанные в этом пункте, являются встроенными операторами, как описано в пункте 5.Когда один из этих операторов перегружен (пункт 13) в допустимом контексте, обозначая таким образом определяемую пользователем операторную функцию, выражение обозначает вызов функции, а операнды образуют список аргументов, без подразумеваемой точки последовательности между ними.

Обычно не рекомендуется перегружать эти операторы в C++, если у вас нет особых требований.Вы можете это сделать, но это может нарушить ожидаемое поведение в коде других людей, особенно если эти операторы используются косвенно через создание экземпляров шаблонов с типом, перегружающим эти операторы.

Другие советы

Вычисление короткого замыкания и порядок вычисления являются обязательным семантическим стандартом как в C, так и в C++.

Если бы это было не так, такой код не был бы распространенной идиомой.

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

Раздел 6.5.13 Логический оператор И спецификации C99 (ссылка в формате PDF) говорит

(4).В отличие от бинарного и оператора, оператор &&, оператор гарантирует слева направо;Существует точка последовательности после оценки первого операнда.Если первый операнд сравнивает равным 0, второй операнд не вычисляется.

Аналогично, раздел 6.5.14 Логический оператор ИЛИ говорит

(4) В отличие от бить | Оператор, || Оператор гарантирует оценку слева направо;Существует точка последовательности после оценки первого операнда.Если первый операнд сравнивает неравенство с 0, второй операнд не оценивается.

Аналогичную формулировку можно найти в стандартах C++. проверьте раздел 5.14 в этом черновике.Как отмечает проверяющий в другом ответе, если вы переопределите && или ||, то оба операнда должны быть оценены, поскольку это становится обычным вызовом функции.

Да, это требуется (как порядок оценки, так и короткое замыкание).В вашем примере, если все функции возвращают true, порядок вызовов строго от функции A, затем от функции B и затем от функции C.Используется для этого типа

if(ptr && ptr->value) { 
    ...
}

То же самое для оператора запятой:

// calls a, then b and evaluates to the value returned by b
// which is used to initialize c
int c = (a(), b()); 

Говорят, что между левым и правым операндом &&, ||, , и между первым и вторым/третьим операндом ?: (условный оператор) является «точкой последовательности».Любые побочные эффекты полностью оцениваются до этого момента.Итак, это безопасно:

int a = 0;
int b = (a++, a); // b initialized with 1, and a is 1

Обратите внимание, что оператор запятая не следует путать с синтаксической запятой, используемой для разделения вещей:

// order of calls to a and b is unspecified!
function(a(), b());

Стандарт C++ говорит в 5.14/1:

Оператор && группируется слева направо.Оба операнда неявно преобразуются в тип bool (раздел 4).Результат является истинным, если оба операнда истинны, и ложным в противном случае.В отличие от &, && гарантирует слева направо оценка:второй операнд не оценивается, если первый операнд является ложным.

И в 5.15/1:

|| группы операторов слева направо.Оба операнда неявно преобразуются в bool (пункт 4).Он возвращает true, если любой из его операндов true, и false в противном случае.В отличие от |, || гарантирует оценку слева направо;более того, второй операнд не оценивается, если первый операнд имеет значение true.

Для обоих рядом с ними написано:

Результатом является bool.Все побочные эффекты первого выражения, за исключением уничтожения временных объектов (12.2), происходят до вычисления второго выражения.

В дополнение к этому, 1.9/18 говорит

При вычислении каждого из выражений

  • a && b
  • a || b
  • a ? b : C
  • a , b

используя встроенное значение операторов в этих выражениях (5.14, 5.15, 5.16, 5.18), после вычисления первого выражения возникает точка последовательности.

Прямо из старого доброго K & R:

  

C гарантирует, что & amp; и || будут оцениваться слева направо & # 8212; мы скоро увидим случаи, когда это имеет значение.

Будьте очень, очень осторожны.

Для фундаментальных типов это операторы быстрого доступа.

Но если вы определяете эти операторы для своего собственного класса или типа перечисления, они не являются ярлыками. Из-за этого семантического различия в их использовании в этих различных обстоятельствах рекомендуется не определять эти операторы.

Для операторов & amp; и || для базовых типов порядок вычисления слева направо (в противном случае сокращение будет затруднено :-) Но для перегруженные операторы, которые вы определяете, являются в основном синтаксическим сахаром для определения метода, и поэтому порядок оценки параметров не определен.

Если вы доверяете Википедии:

  

[ & amp; и || ] семантически отличаются от побитовых операторов & amp; и | потому что они никогда не оценят правый операнд, если результат может быть определен только из левого

http://en.wikipedia.org/wiki/C_(programming_language) #Characteristics

Ваш вопрос сводится к приоритету операторов C ++ и ассоциативности. По сути, в выражениях с несколькими операторами и без скобок компилятор создает дерево выражений, следуя этим правилам.

Для приоритета, когда у вас есть что-то вроде A op1 B op2 C , вы можете сгруппировать вещи как (A op1 B) op2 C или A op1 (B op2 C) . Если op1 имеет более высокий приоритет, чем op2 , вы получите первое выражение. В противном случае вы получите второй.

Для ассоциативности, когда у вас есть что-то вроде A op B op C , вы можете снова сгруппировать thins как (A op B) op C или A op (B op C) . Если op оставил ассоциативность, мы получим первое выражение. Если оно имеет правильную ассоциативность, мы получим второй. Это также работает для операторов с одинаковым уровнем приоритета.

В этом конкретном случае & amp; & amp; имеет более высокий приоритет, чем || , поэтому выражение будет оценено как (a! = " " ; & amp; it == seqMap.end ()) || ISEVEN .

Сам ордер "слева направо" в форме дерева выражений. Поэтому мы сначала оценим a! = & Quot; " & Amp; & Amp; это == seqMap.end () . Если это правда, все выражение верно, в противном случае мы переходим к isEven . Процедура повторяется рекурсивно внутри левого подвыражения, конечно.

<Ч>

Интересные лакомые кусочки, но понятие приоритета коренится в математических обозначениях. То же самое происходит в a * b + c , где * имеет более высокий приоритет, чем + .

Еще более интересно / неясно, для непереносимого выражения A1 op1 A2 op2 ... opn-1 An , где все операторы имеют одинаковый приоритет, число деревьев двоичных выражений, которые мы могли бы сформировать, равно предоставленные так называемыми каталонскими номерами . Для больших n они растут очень быстро. д

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top