Есть ли стандарт ANSI мандат логических операторов закоротить, в С или C ++?
Я смущен, потому что я помню книгу K&R, в которой говорилось, что ваш код не должен зависеть от короткого замыкания этих операций, поскольку они могут и не быть. Может ли кто-нибудь указать, где в стандарте сказано, что логические операции всегда закорачиваются? Меня больше всего интересует C ++, было бы здорово ответить и на C.
Я также помню, как читал (не помню, где), что порядок оценки строго не определен, поэтому ваш код не должен зависеть или предполагать, что функции в выражении будут выполняться в определенном порядке: к концу оператора все функции, на которые есть ссылки будет вызван, но компилятор может выбрать наиболее эффективный порядок.
Указывает ли стандарт порядок оценки этого выражения?
if( functionA() && functionB() && functionC() ) cout<<"Hello world";
источник
Ответы:
Да, короткие замыкания и порядок оценки требуются для операторов
||
и&&
в стандартах C и C ++.Стандарт C ++ говорит (в стандарте C должно быть эквивалентное предложение):
В C ++ есть дополнительная ловушка: короткое замыкание НЕ применяется к типам, которые перегружают операторы
||
и&&
.Обычно не рекомендуется перегружать эти операторы в 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) говорит:
Точно так же в разделе 6.5.14 Оператор логического ИЛИ говорит
Подобную формулировку можно найти в стандартах C ++, см. Раздел 5.14 в этом черновике . Как отмечает проверяющий в другом ответе, если вы переопределите && или ||, тогда оба операнда должны быть оценены, поскольку это становится обычным вызовом функции.
источник
Да, это требует (и порядок оценки, и короткое замыкание). В вашем примере, если все функции возвращают истину, порядок вызовов строго от 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
:И в
5.15/1
:Он говорит для обоих рядом с этими:
В дополнение к этому,
1.9/18
говоритисточник
Прямо из старого доброго K&R:
источник
Будьте очень осторожны.
Для основных типов это операторы быстрого доступа.
Но если вы определяете эти операторы для своего собственного класса или типов перечисления, они не являются ярлыками. Из-за этой семантической разницы в их использовании в этих разных обстоятельствах рекомендуется не определять эти операторы.
Для
operator &&
иoperator ||
для основных типов порядок оценки слева направо ( в противном случае короткой резки будет трудно :-) Но для перегруженных операторов , которые определяют, в основном это синтаксический сахар для определения метода и , таким образом , порядок оценки параметров неопределенный.источник
Ваш вопрос сводится к приоритету и ассоциативности операторов C ++ . Обычно в выражениях с несколькими операторами и без скобок компилятор строит дерево выражений, следуя этим правилам.
Для приоритета, когда у вас есть что-то вроде
A op1 B op2 C
, вы можете сгруппировать вещи как(A op1 B) op2 C
илиA op1 (B op2 C)
. Еслиop1
имеет более высокий приоритет, чемop2
, вы получите первое выражение. В противном случае вы получите второй.Для ассоциативности, когда у вас есть что-то вроде
A op B op C
, вы можете снова сгруппировать тонкие как(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
они растут очень быстро. dисточник
Если вы доверяете Википедии:
C (язык программирования)
источник