Требуются ли логические операторы короткого замыкания? А порядок оценки?

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

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

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

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

if( functionA() && functionB() && functionC() ) cout<<"Hello world";

7 ответов


да, короткое замыкание и порядок оценки необходимы для операторов || и && в стандартах 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 != '')) {
      // do something useful

   }

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

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

аналогично, секция 6.5.14 логический оператор или говорит

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

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


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

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). Результат равен true, если оба операнда являются true и false в противном случае. В отличие от &, && гарантии слева направо оценка: второй операнд не вычисляется, если первый операнд имеет значение false.

и 5.15/1:

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

он говорит для обоих рядом с теми:

результатом является 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 гарантирует, что && и || оцениваются слева направо-мы скоро увидим случаи, когда это имеет значение.


будьте очень-очень осторожны.

для типов POD это операторы быстрого доступа.

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

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


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

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

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 оставил ассоциативность, мы заканчиваем с первым выражением. Если у него есть правильная ассоциативность, мы получаем вторую. Это также работает для операторов на том же уровне приоритета.

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

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


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

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