Являются ли закодированные логические операторы короткими законами? И порядок оценки?

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

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

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

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

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

Ответы

Ответ 1

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

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

1.9.18

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

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

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

В С++ есть дополнительная ловушка: short-circuiting делает NOT применительно к типам, которые перегружают операторы || и &&.

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

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

Ответ 2

Оценка короткого замыкания и порядок оценки - это обязательный семантический стандарт как в 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 Логический оператор AND спецификации C99 (ссылка в формате PDF) говорит

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

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

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

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

Ответ 3

Да, он предусматривает, что (как порядок оценки, так и короткое замыкание). В вашем примере, если все функции возвращают true, порядок вызовов выполняется строго из функции A, затем 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());

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

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

И в 5.15/1:

|| группы операторов слева направо. Операнды оба неявно преобразуются в bool (раздел 4). Он возвращает 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), после оценки первого выражения существует точка последовательности.

Ответ 4

Прямо от старого старого K & R:

C гарантирует, что && и || оцениваются слева направо - мы скоро увидим случаи, когда это имеет значение.

Ответ 5

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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

Для приоритета, когда у вас есть что-то вроде 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, где * имеет более высокий приоритет, чем +.

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