c++ - ejemplo - return operador ternario
¿Por qué el operador ternario con comas evalúa solo una expresión en el caso verdadero? (5)
Actualmente estoy aprendiendo C ++ con el libro C ++ Primer y uno de los ejercicios en el libro es:
Explica qué hace la siguiente expresión:
someValue ? ++x, ++y : --x, --y
someValue ? ++x, ++y : --x, --y
¿Qué sabemos? Sabemos que el operador ternario tiene mayor prioridad 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 operadores binarios "tener mayor prioridad" significa que podemos usar paréntesis alrededor de la expresión con mayor prioridad y no cambiará la ejecución.
Para el operador ternario, haría:
(someValue ? ++x, ++y : --x, --y)
resultando efectivamente en el mismo código que no me ayuda a entender cómo el compilador agrupará el código.
Sin embargo, a partir de las pruebas 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 el operador ternario correctamente.
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, con
someValue = false
imprime:
9 9
¿Por qué el compilador de C ++ generaría un código que para la rama verdadera del operador ternario solo incrementa
x
, mientras que para la rama falsa del ternario disminuye tanto
x
como
y
?
Incluso fui tan lejos como poner paréntesis alrededor de la rama verdadera de esta manera:
someValue ? (++x, ++y) : --x, --y;
pero todavía resulta en
11 10
.
¿Por qué el compilador de C ++ generaría código que para la rama verdadera del operador ternario solo incrementa
x
Usted malinterpretó lo que sucedió.
La rama verdadera incrementa tanto
x
como
y
.
Sin embargo,
y
se decrementa inmediatamente después de eso, incondicionalmente.
Así es como sucede esto: dado que el operador condicional tiene mayor prioridad que el operador de coma en C ++ , el compilador analiza la expresión de la siguiente manera:
(someValue ? ++x, ++y : --x), (--y);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^
Tenga en cuenta el "huérfano"
--y
después de la coma.
Esto es lo que lleva a disminuir
y
que se ha incrementado inicialmente.
Incluso fui tan lejos como poner paréntesis alrededor de la rama verdadera de esta manera:
someValue ? (++x, ++y) : --x, --y;
Estaba en el camino correcto, pero paréntesis de una rama incorrecta: puede solucionar esto al paréntesis de la rama else, de esta manera:
someValue ? ++x, ++y : (--x, --y);
Como dijo @Rakete en su excelente respuesta, esto es complicado. Me gustaría agregar a eso un poco.
El operador ternario debe tener la forma:
¿lógica o expresión
?
expresión:
asignación-expresión
Entonces tenemos las siguientes asignaciones:
-
someValue
: lógico o expresión -
++x, ++y
: expresión -
???
¿es
la expresión de asignación
--x, --y
o solo--x
?
De hecho, solo es
--x
porque una
expresión de asignación
no puede analizarse como dos expresiones separadas por una coma (de acuerdo con las reglas gramaticales de C ++), por lo que
--x, --y
no puede tratarse como una
expresión de asignación
.
Lo que da como resultado que la porción de expresión ternaria (condicional) se vea así:
someValue?++x,++y:--x
Puede ser útil, en aras de la legibilidad, considerar que
++x,++y
se calculen
como si estuvieran
entre paréntesis
(++x,++y)
;
algo contenido entre
?
y
:
se secuenciará
después
del condicional.
(Los paréntesis para el resto de la publicación).
y evaluado en este orden:
-
someValue?
-
(++x,++y)
o--x
(dependiendo del resultado debool
de 1.)
Esta expresión se trata como la
--y
izquierda de un operador de coma, siendo la
--y
derecha
--y
, así:
(someValue?(++x,++y):--x), --y;
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 devolvemos eso.
Entonces, ¿qué sucede cuando
someValue
es
true
?
-
(someValue?(++x,++y):--x)
ejecuta e incrementax
ey
para ser11
y11
- La expresión izquierda se descarta (aunque los efectos secundarios del incremento permanecen)
-
Evaluamos el lado derecho del operador de coma:
--y
, que luego disminuyey
nuevamente a10
Para "arreglar" el comportamiento, puede agrupar
--x, --y
con paréntesis para transformarlo en una
expresión primaria
que
es
una entrada válida para una
expresión de asignación
*:
someValue?++x,++y:(--x, --y);
* Es una cadena larga bastante divertida que conecta una expresión de asignación a una expresión primaria:
asignación-expresión --- (puede consistir en) -> expresión condicional -> lógica-o-expresión -> lógica-y-expresión -> inclusiva-o-expresión -> exclusiva-o-expresión - -> y-expresión -> igualdad-expresión -> expresión-relacional -> desplazamiento-expresión -> expresión-aditiva -> expresión-multiplicativa -> expresión-pm -> expresión-cast -> expresión unaria -> expresión-postfix -> expresión-primaria
Su problema es que la expresión ternaria realmente no tiene mayor prioridad que la coma. De hecho, C ++ no se puede describir con precisión simplemente por precedencia, y es exactamente la interacción entre el operador ternario y la coma donde se descompone.
a ? b++, c++ : d++
se trata como:
a ? (b++, c++) : d++
(la coma se comporta como si tuviera mayor precedencia). Por otra parte,
a ? b++ : c++, d++
se trata como:
(a ? b++ : c++), d++
y el operador ternario tiene mayor prioridad.
Un punto que se ha pasado por alto en las respuestas (aunque se menciona en los comentarios) es que el operador condicional se usa invariablemente (¿previsto por diseño?) En el código real como un atajo para asignar uno de los dos valores a una variable.
Entonces, el contexto más amplio sería:
whatIreallyWanted = someValue ? ++x, ++y : --x, --y;
Lo cual es absurdo, por lo que los crímenes son múltiples:
- El lenguaje permite efectos secundarios ridículos en una tarea.
- El compilador no te advirtió que estabas haciendo cosas extrañas.
- El libro parece estar centrado en preguntas "engañosas". Uno solo puede esperar que la respuesta en la parte de atrás sea "Lo que hace esta expresión es depender de casos extraños en un ejemplo artificial para producir efectos secundarios que nadie espera. Nunca haga esto".
Wow, eso es complicado.
El compilador ve tu expresión como:
(someValue ? (++x, ++y) : --x), --y;
El operador ternario necesita un
:
no puede sostenerse por sí mismo en ese contexto, pero después de eso, no hay ninguna razón por la cual la coma debe pertenecer al caso falso.
Ahora podría tener más sentido por qué obtienes esa salida.
Si
someValue
es verdadero, entonces
++x
,
++y
y
--y
se ejecutan, lo que no cambia efectivamente
y
agrega uno a
x
.
Si
someValue
es falso, entonces
--x
y
--y
se ejecutan, disminuyéndolos por uno.