O padrão ANSI exige que os operadores lógicos sofram um curto-circuito, em C ou C ++?
Estou confuso, pois lembro do livro da K&R dizendo que seu código não deveria depender de curto-circuito dessas operações, pois talvez não. Alguém poderia apontar onde, no padrão, as operações lógicas são sempre em curto-circuito? Estou interessado principalmente em C ++, uma resposta também para C seria ótima.
Também me lembro de ler (não lembro onde) que a ordem de avaliação não é estritamente definida; portanto, seu código não deve depender ou assumir funções dentro de uma expressão que serão executadas em uma ordem específica: no final de uma instrução, todas as funções referenciadas terá sido chamado, mas o compilador tem liberdade para selecionar a ordem mais eficiente.
O padrão indica a ordem de avaliação dessa expressão?
if( functionA() && functionB() && functionC() ) cout<<"Hello world";
fonte
Respostas:
Sim, a ordem de curto-circuito e avaliação é necessária para os operadores
||
e&&
nos padrões C e C ++.O padrão C ++ diz (deve haver uma cláusula equivalente no padrão C):
Em C ++, há uma armadilha extra: curto-circuito NÃO se aplica a tipos que sobrecarregam operadores
||
e&&
.Geralmente, não é recomendável sobrecarregar esses operadores em C ++, a menos que você tenha um requisito muito específico. Você pode fazê-lo, mas pode quebrar o comportamento esperado no código de outras pessoas, especialmente se esses operadores forem usados indiretamente por meio de modelos de instanciação com o tipo sobrecarregando esses operadores.
fonte
A avaliação de curto-circuito e a ordem da avaliação são um padrão semântico obrigatório em C e C ++.
Se não fosse, código como este não seria um idioma comum
A Seção 6.5.13 O operador AND lógico da especificação C99 (link em PDF) diz
Da mesma forma, a seção 6.5.14 O operador lógico OU diz
Palavras semelhantes podem ser encontradas nos padrões C ++, consulte a seção 5.14 neste rascunho . Como observa o verificador em outra resposta, se você substituir && ou ||, os dois operandos deverão ser avaliados à medida que se tornar uma chamada de função regular.
fonte
Sim, exige isso (ordem de avaliação e curto-circuito). No seu exemplo, se todas as funções retornarem verdadeiras, a ordem das chamadas será estritamente de functionA, functionB e functionC. Usado para isso como
O mesmo para o operador de vírgula:
Diz-se entre o lado esquerdo e operando à direita de
&&
,||
,,
e entre o primeiro e segundo / terceiro operando de?:
(operador condicional) é um "ponto de sequência". Quaisquer efeitos colaterais são avaliados completamente antes desse ponto. Então, isso é seguro:Observe que o operador de vírgula não deve ser confundido com a vírgula sintática usada para separar as coisas:
O padrão C ++ diz em
5.14/1
:E em
5.15/1
:Diz para os dois próximos a esses:
Além disso,
1.9/18
dizfonte
Direto da boa e velha K&R:
fonte
Tenha muito, muito cuidado.
Para tipos fundamentais, esses são operadores de atalho.
Mas se você definir esses operadores para sua própria classe ou tipos de enumeração, eles não serão atalhos. Devido a essa diferença semântica em seu uso nessas diferentes circunstâncias, é recomendável que você não defina esses operadores.
Para os tipos
operator &&
eoperator ||
para os fundamentais, a ordem de avaliação é da esquerda para a direita (caso contrário, o atalho seria difícil :-) Mas para os operadores sobrecarregados que você define, estes são basicamente o açúcar sintático para definir um método e, portanto, a ordem de avaliação dos parâmetros é Indefinido.fonte
Sua pergunta se resume à precedência e associatividade do operador C ++ . Basicamente, em expressões com vários operadores e sem parênteses, o compilador constrói a árvore de expressões seguindo estas regras.
Por precedência, quando você tem algo parecido
A op1 B op2 C
, pode agrupar as coisas como(A op1 B) op2 C
ouA op1 (B op2 C)
. Seop1
tiver maior precedência do queop2
, você obterá a primeira expressão. Caso contrário, você receberá o segundo.Por associatividade, quando você tem algo parecido
A op B op C
, pode agrupar novamente como(A op B) op C
ouA op (B op C)
. Seop
deixou a associatividade, terminamos com a primeira expressão. Se tiver associatividade correta, terminamos com a segunda. Isso também funciona para operadores no mesmo nível de precedência.Nesse caso em particular,
&&
tem precedência mais alta que||
, portanto, a expressão será avaliada como(a != "" && it == seqMap.end()) || isEven
.A ordem em si é "da esquerda para a direita" no formulário da árvore de expressão. Então, primeiro avaliaremos
a != "" && it == seqMap.end()
. Se é verdade, toda a expressão é verdadeira; caso contrário, vamos paraisEven
. O procedimento se repete recursivamente dentro da subexpressão esquerda, é claro.Boatos interessantes, mas o conceito de precedência tem suas raízes na notação matemática. O mesmo acontece em
a*b + c
, onde*
tem maior precedência do que+
.Ainda mais interessante / obscuro, para uma expressão sem parênteses
A1 op1 A2 op2 ... opn-1 An
, em que todos os operadores têm a mesma precedência, o número de árvores de expressão binária que poderíamos formar é dado pelos chamados números catalães . Para grandesn
, elas crescem extremamente rápido. dfonte
Se você confia na Wikipedia:
C (linguagem de programação)
fonte