Actualmente estoy aprendiendo C ++ con el libro C ++ Primer y uno de los ejercicios del libro es:
Explica qué hace la siguiente expresión:
someValue ? ++x, ++y : --x, --y
¿Qué sabemos? Sabemos que el operador ternario tiene mayor precedencia que el operador de coma. Con los operadores binarios esto fue bastante fácil de entender, pero con el operador ternario estoy luchando un poco. Con los operadores binarios "tener mayor precedencia" significa que podemos usar paréntesis alrededor de la expresión con mayor precedencia y no cambiará la ejecución.
Para el operador ternario haría:
(someValue ? ++x, ++y : --x, --y)
dando como resultado efectivamente el mismo código, lo que no me ayuda a comprender cómo el compilador agrupará el código.
Sin embargo, al probar con un compilador de C ++, sé que la expresión se compila y no sé qué :
podría representar un operador por sí mismo. Entonces, el compilador parece interpretar correctamente el operador ternario.
Luego ejecuté el programa de dos maneras:
#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;
}
Resultados en:
11 10
Mientras que por otro lado someValue = false
imprime:
9 9
¿Por qué el compilador de C ++ generaría código que para la rama verdadera del operador ternario solo aumenta x
, mientras que para la rama falsa del ternario disminuye tanto x
y y
?
Incluso fui tan lejos como para poner paréntesis alrededor de la rama verdadera de esta manera:
someValue ? (++x, ++y) : --x, --y;
pero todavía resulta en 11 10
.
fuente
?
es el operador condicional . El término operador ternario simplemente significa un operador con tres operandos. El operador condicional es un ejemplo de un operador ternario, pero un lenguaje podría (teóricamente) tener múltiples operadores ternarios.Respuestas:
Como dijo @Rakete en su excelente respuesta, esto es complicado. Me gustaría agregar un poco más a eso.
El operador ternario debe tener la forma:
Entonces tenemos las siguientes asignaciones:
someValue
: lógica-o-expresión++x, ++y
: expresión--x, --y
o solo--x
?De hecho, es solo
--x
porque una expresión de asignación no se puede analizar como dos expresiones separadas por una coma (de acuerdo con las reglas gramaticales de C ++), por--x, --y
lo que no se puede tratar como una expresión de asignación .Lo que da como resultado que la porción de expresión ternaria (condicional) se vea así:
Puede ser útil en aras de la legibilidad considerar
++x,++y
que se calcula como si estuviera entre paréntesis(++x,++y)
; todo lo contenido entre?
y:
se secuenciará después del condicional. (Los pondré entre paréntesis para el resto de la publicación).y evaluado en este orden:
someValue?
(++x,++y)
o--x
(dependiendo delbool
resultado de 1.)Esta expresión luego se trata como la subexpresión izquierda de un operador de coma, siendo la subexpresión derecha
--y
, así:Lo que significa que el lado izquierdo es una expresión de valor descartado , lo que significa que definitivamente se evalúa, pero luego evaluamos el lado derecho y lo devolvemos.
Entonces, ¿qué pasa cuando
someValue
estrue
?(someValue?(++x,++y):--x)
se ejecuta e incrementax
yy
para ser11
y11
--y
que luegoy
regresa a10
Para "corregir" el comportamiento, puede agruparlo
--x, --y
con paréntesis para transformarlo en una expresión primaria que sea una entrada válida para una expresión de asignación *:* Es una cadena larga bastante divertida que conecta una expresión de asignación con una expresión primaria:
asignación-expresión --- (puede consistir en) -> condicional-expresión -> lógico-o-expresión -> lógico-y-expresión -> inclusive-o-expresión -> exclusiva-o-expresión - -> expresión-y -> expresión-igualdad -> expresión-relacional -> expresión-cambio -> expresión-aditiva -> expresión-multiplicativa -> expresión-pm -> expresión-moldeada -> expresión-unaria -> expresión-postfijo -> expresión-primaria
fuente
{ ... }
pueden tratarse como una expresión), ahora tengo una respuesta => es para evitar tener que introducir un operador de coma que se comporta de formas tan complicadas.assignment-expression
cadena?Vaya, eso es complicado.
El compilador ve su expresión como:
El operador ternario necesita a
:
, no puede sostenerse por sí mismo en ese contexto, pero después de él, no hay ninguna razón por la que la coma deba pertenecer al caso falso.Ahora podría tener más sentido por qué obtiene ese resultado. Si
someValue
es cierto, entonces++x
,++y
y--y
ejecute, que no cambia efectivamentey
pero agrega uno ax
.Si
someValue
es falso, entonces--x
y--y
se ejecutan, disminuyendo ambos en uno.fuente
Usted malinterpretó lo que sucedió. La rama verdadera incrementa tanto
x
yy
. Sin embargo,y
se decrementa inmediatamente después de eso, incondicionalmente.Así es como sucede esto: dado que el operador condicional tiene mayor precedencia que el operador de coma en C ++ , el compilador analiza la expresión de la siguiente manera:
Tenga en cuenta el "huérfano"
--y
después de la coma. Esto es lo que lleva a decrementary
inicialmente incrementado.Estaba en el camino correcto, pero puso entre paréntesis una rama incorrecta: puede solucionar esto entre paréntesis en la rama else, así:
Demostración (impresiones 11 11)
fuente
Su problema es que la expresión ternaria no tiene mayor precedencia que la coma. De hecho, C ++ no se puede describir con precisión simplemente por la precedencia, y es exactamente la interacción entre el operador ternario y la coma donde se descompone.
se trata como:
(la coma se comporta como si tuviera mayor precedencia). Por otra parte,
se trata como:
y el operador ternario tiene mayor precedencia.
fuente
Un punto que se ha pasado por alto en las respuestas (aunque se ha mencionado en los comentarios) es que el operador condicional se usa invariablemente (¿previsto por diseño?) En código real como un atajo para asignar uno de dos valores a una variable.
Entonces, el contexto más amplio sería:
Lo cual es absurdo a primera vista, por lo que los crímenes son múltiples:
fuente
if
(por ejemplo, la expresión de incremento en un bucle for). El contexto más amplio bien podría serfor (x = 0, y=0; x+y < 100; someValue?(++x, ++y) :( --x, --y))
con un bucle que puede modificarx
yy
de forma independiente.