Pergunta

O padrão ANSI mandato os operadores lógicos para ser um curto-circuito, em C ou C++?

Estou confuso por eu me lembro da K&R livro dizendo que seu código não deve depender operações que são curto-circuitadas, pois eles não podem.Alguém por favor poderia apontar onde o padrão é dito lógica ops são sempre de curto-circuito?Eu estou mais interessado em C++, uma resposta também para C seria ótimo.

Eu também lembro de ler (não lembro onde) que a ordem de avaliação não seja estritamente definida, portanto, seu código não deve depender ou assumir funções dentro de uma expressão poderiam ser executadas em uma ordem específica:até o final de uma declaração referenciado todas as funções foram chamados, mas o compilador não tem liberdade para escolher a mais eficiente.

O padrão pode indicar a ordem de avaliação da expressão?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";
Foi útil?

Solução

Sim, curto-circuito e ordem de avaliação são necessários para operadores || e && em C e C++ padrões.

Padrão de C++ diz (não deve ser um equivalente de cláusula C padrão):

1.9.18

Na avaliação das seguintes expressões

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

usando o built-in significado dos operadores nessas expressões, há um ponto de seqüência, após a avaliação da primeira expressão (12).

Em C++ não é um extra armadilha:curto-circuito não NÃO aplicar a tipos de sobrecarga de operadores || e &&.

Nota de rodapé 12:Os operadores indicados neste parágrafo são os operadores internos, conforme descrito na cláusula 5.Quando um destes operadores é sobrecarregado (cláusula 13) em um contexto válido, portanto, a designação de um usuário definido pelo operador função, a expressão designa uma invocação de função, e os operandos forma de uma lista de argumento, sem uma seqüência obrigatória ponto entre eles.

Geralmente não é recomendado para sobrecarregue esses operadores em C++, a menos que você tem uma necessidade específica.Você pode fazê-lo, mas pode quebrar o esperado comportamento o código de outras pessoas, especialmente se estes operadores são utilizados indiretamente através da instanciação de modelos com o tipo de sobrecarga estes operadores.

Outras dicas

A avaliação de curto -circuito e a ordem de avaliação são um padrão semântico obrigatório em C e C ++.

Se não fosse, código como esse não seria um idioma comum

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

Seção 6.5.13 Operador lógico e da especificação C99 (Link pdf) diz

(4). Ao contrário do binário e operador bitwise, o operador garante a avaliação da esquerda para a direita; Há um ponto de sequência após a avaliação do primeiro operando. Se o primeiro operando se comparar igual a 0, o segundo operando não será avaliado.

Da mesma forma, seção 6.5.14 Lógico ou operador diz

(4) Ao contrário do bit a bit | Operador, o || O operador garante avaliação da esquerda para a direita; Há um ponto de sequência após a avaliação do primeiro operando. Se o primeiro operando se comparar desigual a 0, o segundo operando não será avaliado.

Uma redação semelhante pode ser encontrada nos padrões C ++, Verifique a seção 5.14 nesta cópia do rascunho. Como observa os verificadores em outra resposta, se você substituir && ou ||, ambos os operando deverão ser avaliados à medida que se tornar uma chamada de função regular.

Sim, exige que (ordem de avaliação e curto -circuito). No seu exemplo, se todas as funções retornarem true, a ordem das chamadas é estritamente da functionA e depois functionb e depois functionC. Usado para isso como

if(ptr && ptr->value) { 
    ...
}

O mesmo para o operador de vírgula:

// calls a, then b and evaluates to the value returned by b
// which is used to initialize c
int c = (a(), b()); 

Um diz entre o operando esquerdo e direito de &&, ||, , e entre o primeiro e o 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:

int a = 0;
int b = (a++, a); // b initialized with 1, and a is 1

Observe que o operador de vírgula não deve ser confundido com a vírgula sintática usada para separar as coisas:

// order of calls to a and b is unspecified!
function(a(), b());

O padrão C ++ diz em 5.14/1:

Os grupos de operadores && da esquerda para a direita. Os operandos são implicitamente convertidos ao tipo BOOL (cláusula 4). O resultado é verdadeiro se ambos os operando forem verdadeiros e falsos. Ao contrário de &, && garante avaliação da esquerda para a direita: o segundo operando não será avaliado se o primeiro operando for falso.

E em 5.15/1:

O || Grupos de operadores da esquerda para a direita. Os operandos são implicitamente convertidos em BOOL (cláusula 4). Ele retorna verdadeiro se um de seus operandos for verdadeiro e false de outra forma. Ao contrário |, || garante avaliação da esquerda para a direita; Além disso, o segundo operando não é avaliado se o primeiro operando avaliar como verdadeiro.

Diz para ambos ao lado daqueles:

O resultado é um bool. Todos os efeitos colaterais da primeira expressão, exceto pela destruição de temporários (12.2), acontecem antes que a segunda expressão seja avaliada.

Além disso, 1.9/18 diz

Na avaliação de cada uma das expressões

  • a && b
  • a || b
  • a ? b : C
  • a , b

Usando o significado interno dos operadores nessas expressões (5.14, 5.15, 5.16, 5.18), há um ponto de sequência após a avaliação da primeira expressão.

Diretamente do bom e velho K&R:

C garante isso && e || são avaliados da esquerda para a direita - em breve veremos casos onde isso importa.

Tenha 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 sã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 o operator && e operator || Para tipos fundamentais, a ordem de avaliação é deixada para a direita (caso contrário, o corte curto seria difícil :-) Mas, para os operadores sobrecarregados que você define, esses são basicamente açúcar sintático para definir um método e, portanto, a ordem de avaliação dos parâmetros é indefinida.

Se você confia na Wikipedia:

[&& e ||] são semanticamente distintos dos operadores de bits e e | Porque eles nunca avaliarão o operando correto se o resultado puder ser determinado da esquerda sozinha

http://en.wikipedia.org/wiki/C_(Programming_Language)#Characterists

Sua pergunta se resume a Precedência do operador C ++ e associatividade. Basicamente, em expressões com vários operadores e sem parênteses, o compilador constrói a árvore de expressão seguindo essas regras.

Por precedência, quando você tem algo como A op1 B op2 C, você pode agrupar as coisas como (A op1 B) op2 C ou A op1 (B op2 C). Se op1 tem maior precedência do que op2, você receberá a primeira expressão. Caso contrário, você receberá o segundo.

Para a associatividade, quando você tem algo como A op B op C, você pode novamente agrupar os discussões como (A op B) op C ou A op (B op C). Se op deixou a associatividade, acabamos com a primeira expressão. Se tiver associativa correta, acabamos com o segundo. Isso também funciona para operadores no mesmo nível de precedência.

Neste caso em particular, && tem maior precedência do que ||, então a expressão será avaliada como (a != "" && it == seqMap.end()) || isEven.

A ordem em si é "da esquerda para a direita" na forma de árvore de expressão. Então, primeiro avaliaremos a != "" && it == seqMap.end(). Se for verdade, toda a expressão é verdadeira, caso contrário, vamos para isEven. O procedimento se repete recursivamente dentro da subexpressão esquerda, é claro.


Pedidos 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 parente A1 op1 A2 op2 ... opn-1 An, onde todos os operadores têm a mesma precedência, o número de árvores de expressão binária que poderíamos formar é dado pelo chamado Números catalães. Para grande n, estes crescem extremamente rápido. d

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top