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