Atualmente, estou aprendendo C ++ com o livro C ++ Primer e um dos exercícios do livro é:
Explique o que a seguinte expressão faz:
someValue ? ++x, ++y : --x, --y
O que nós sabemos? Sabemos que o operador ternário tem uma precedência mais alta que o operador de vírgula. Com operadores binários, isso era muito fácil de entender, mas com o operador ternário eu estou lutando um pouco. Com operadores binários "ter maior precedência" significa que podemos usar parênteses em torno da expressão com maior precedência e isso não mudará a execução.
Para o operador ternário, eu faria:
(someValue ? ++x, ++y : --x, --y)
resultando efetivamente no mesmo código que não me ajuda a entender como o compilador agrupará o código.
No entanto, ao testar com um compilador C ++, sei que a expressão é compilada e não sei o que um :
operador pode representar por si só. Portanto, o compilador parece interpretar o operador ternário corretamente.
Então eu executei o programa de duas maneiras:
#include <iostream>
int main()
{
bool someValue = true;
int x = 10, y = 10;
someValue ? ++x, ++y : --x, --y;
std::cout << x << " " << y << std::endl;
return 0;
}
Resulta em:
11 10
Enquanto, por outro lado someValue = false
, imprime:
9 9
Por que o compilador C ++ geraria código que apenas para o ramo verdadeiro do operador ternário é incrementado x
, enquanto para o ramo falso do ternário ele diminui tanto x
e y
?
Até cheguei a colocar parênteses em torno do ramo verdadeiro assim:
someValue ? (++x, ++y) : --x, --y;
mas ainda resulta em 11 10
.
fonte
?
é o operador condicional . O termo operador ternário significa simplesmente um operador com três operandos. O operador condicional é um exemplo de operador ternário, mas uma linguagem pode (teoricamente) ter vários operadores ternários.Respostas:
Como o @Rakete disse em sua excelente resposta, isso é complicado. Eu gostaria de acrescentar um pouco a isso.
O operador ternário deve ter a forma:
Portanto, temos os seguintes mapeamentos:
someValue
: expressão lógica ou++x, ++y
: expressão--x, --y
ou apenas--x
?De fato, é apenas
--x
porque uma expressão de atribuição não pode ser analisada como duas expressões separadas por vírgula (de acordo com as regras gramaticais do C ++), portanto,--x, --y
não pode ser tratada como uma expressão de atribuição .O que resulta na parte da expressão ternária (condicional) da seguinte forma:
Para facilitar a leitura,
++x,++y
pode ser útil calcular como entre parênteses(++x,++y)
; qualquer coisa contida entre?
e:
será sequenciada após a condicional. (Vou colocá-los entre parênteses no restante da postagem).e avaliados nesta ordem:
someValue?
(++x,++y)
ou--x
(dependendo dobool
resultado de 1.)Essa expressão é então tratada como a subexpressão esquerda de um operador de vírgula, com a subexpressão direita sendo
--y
assim:O que significa que o lado esquerdo é uma expressão de valor descartado , o que significa que é definitivamente avaliada, mas avaliamos o lado direito e o retornamos.
Então, o que acontece quando
someValue
étrue
?(someValue?(++x,++y):--x)
executa e incrementax
ey
para ser11
e11
--y
que depois diminui dey
volta para10
Para "consertar" o comportamento, você pode agrupar
--x, --y
parênteses para transformá-lo em uma expressão principal, que é uma entrada válida para uma expressão de atribuição *:* É uma cadeia longa bastante engraçada que conecta uma expressão de atribuição a uma expressão primária:
expressão de atribuição --- (pode consistir em) -> expressão condicional -> expressão ou lógica -> expressão e lógica -> expressão ou inclusão -> expressão ou inclusão -> expressão ou exclusiva - -> and-expression -> igualdade-expressão -> expressão relacional -> shift-expression -> expressão aditiva -> expressão aditiva -> expressão multiplicativa -> expressão pm -> expressão express -> expressão unária -> expressão postfix -> expressão primária
fonte
{ ... }
podem ser tratadas como expressões), agora eu tenho uma resposta => é para evitar ter que introduzir um operador de vírgula que se comporte de maneiras tão complicadas.assignment-expression
cadeia?Uau, isso é complicado.
O compilador vê sua expressão como:
O operador ternário precisa de a
:
, não pode permanecer por si só nesse contexto, mas depois disso, não há razão para que a vírgula deva pertencer ao caso falso.Agora, pode fazer mais sentido o motivo pelo qual você obtém essa saída. Se
someValue
for verdade, então++x
,++y
e--y
seja executado, o que não muda efetivamente,y
mas adiciona um ax
.Se
someValue
for falso, então--x
e--y
são executados, diminuindo os dois por um.fonte
Você interpretou mal o que aconteceu. O ramo verdadeiro incrementa ambos
x
ey
. Contudo,y
é diminuído imediatamente depois disso, incondicionalmente.Aqui está como isso acontece: como o operador condicional tem maior precedência do que o operador vírgula em C ++ , o compilador analisa a expressão da seguinte maneira:
Observe o "órfão"
--y
após a vírgula. Isso é o que leva à diminuiçãoy
que foi inicialmente incrementada.Você estava no caminho certo, mas colocou um ramo errado entre parênteses: você pode corrigi-lo entre parênteses do ramo else, assim:
Demo (impressões 11 11)
fonte
Seu problema é que a expressão ternária realmente não tem precedência mais alta que vírgula. De fato, o C ++ não pode ser descrito com precisão simplesmente por precedência - e é exatamente a interação entre o operador ternário e a vírgula onde ocorre a quebra.
é tratado como:
(vírgula se comporta como se tivesse maior precedência). Por outro lado,
é tratado como:
e o operador ternário tem maior precedência.
fonte
Um ponto que foi esquecido nas respostas (embora abordado nos comentários) é que o operador condicional é invariavelmente usado (destinado ao design?) No código real como um atalho para atribuir um dos dois valores a uma variável.
Portanto, o contexto maior seria:
O que é absurdo, por isso os crimes são múltiplos:
fonte
if
(por exemplo, a expressão de incremento em um loop). O contexto maior pode muito bem estarfor (x = 0, y=0; x+y < 100; someValue?(++x, ++y) :( --x, --y))
com um loop que pode modificarx
e de formay
independente.