c++ ternary-operator operator-precedence

c++ - ¿Cómo se determina el tipo de retorno de un operador ternario?



ternary-operator operator-precedence (6)

Debe poner paréntesis alrededor de una operación ternaria:

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

Si no, el operador << va al 2 y da un error porque no tiene esa función sobrecargada.

Esto sucede porque el operador de desplazamiento a la izquierda ( << ) tiene una mayor prioridad que el operador ternario. Puede ver la lista completa de operadores y su precedencia en en.cppreference.com/w/cpp/language/operator_precedence .

Esta pregunta ya tiene una respuesta aquí:

Estaba resolviendo un problema sobre un obispo en un tablero de ajedrez. En un punto de mi código, incluí la siguiente línea:

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;

Esto genera el siguiente error:

error: invalid operands of types ''int'' and ''<unresolved overloaded function type>'' to binary ''operator<<''

Sin embargo, arreglé instantáneamente este error al incluir una variable adicional en mi código:

int steps = (abs(c2-c1) == abs(r2-r1)) ? 1 : 2; std::cout << steps << std::endl;

¿Cómo funciona el operador ternario y cómo se determina su tipo de retorno (como el compilador lo llamó <unresolved overloaded function type> )?


Debido a la precedencia del operador , esa línea se trata como:

(std::cout << (abs(c2-c1) == abs(r2-r1))) ? 1 : (2 << std::endl);

Cámbialo a

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl; // ^----------------------------------^ // Surrounding parentheses


Es fácil ver el error cuando se visualiza el orden de análisis:

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl; /_______/ <--- #1 /________________________/ V /~~~~error~~~/ <--- #2 /_____________________________________________/ <--- #3 /__________________________________________________________/ <--- #4 /___________________________________________________________/ <--- #5


Esto no tiene nada que ver con cómo se deduce el tipo de retorno y todo que ver con la en.cppreference.com/w/cpp/language/operator_precedence . Cuando tengas

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;

no es

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

porque ?: tiene menor prioridad que << . Eso significa que lo que realmente tienes es

(std::cout << (abs(c2-c1) == abs(r2-r1))) ? 1 : (2 << std::endl);

y esta es la razón por la que obtiene un error sobre un <unresolved overloaded function type> . Solo usa paréntesis como

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

y estarás bien


La respuesta literal a la pregunta que hizo es el algoritmo en la sección [expr.cond] del estándar del lenguaje C ++.

La regla básica "determinó si se puede formar una secuencia de conversión implícita del segundo operando al tipo de destino determinado para el tercer operando, y viceversa". Si no hay una conversión posible, o si hay más de una, es un error de sintaxis, pero hay varios casos especiales (que no se aplican aquí):

  • Si ambos son aritméticos o tipos de enum , ¿obtiene el mismo tipo de conversión implícita para p ? a : b p ? a : b que determina el tipo de una expresión como a + b o a * b .
  • Uno de los objetivos puede ser una expresión de throw y se trata como si tuviera el tipo de la otra.
  • Si uno de los objetivos es un campo de bits, también lo es el tipo de expresión condicional
  • Los punteros con diferentes calificadores (como const y volatile ) tienen sus calificadores unificados.

El resultado es un valor de gl si los objetivos son valores de gl del mismo tipo, y un valor de lo contrario.

En caso de duda, siempre puede emitir explícitamente uno o ambos operandos para que tengan el mismo tipo.

Su problema real aquí es la precedencia del operador, como explica la respuesta aceptada. Es decir, el compilador analiza el tercer operando como 2 << std::endl , en lugar de 2.


Según en.cppreference.com/w/cpp/language/operator_precedence :

Al analizar una expresión, un operador que aparece en una fila de la tabla anterior con una prioridad estará más ajustado (como entre paréntesis) a sus argumentos que cualquier operador que se enumere en una fila más abajo con una prioridad más baja. Por ejemplo, las expresiones std::cout << a & b y *p++ se analizan como (std::cout << a) & b y *(p++) , y no como std::cout << (a & b) o (*p)++ .

Los operadores que tienen la misma precedencia están vinculados a sus argumentos en la dirección de su asociatividad. Por ejemplo, la expresión a = b = c se analiza como a = (b = c) , y no como (a = b) = c debido a la asociatividad de asignación de derecha a izquierda, pero se analiza a a + b - c (a + b) - c y no a + (b - c) debido a la asociatividad de suma y resta de izquierda a derecha.

La especificación de asociatividad es redundante para operadores unarios y solo se muestra para completar: los operadores de prefijo unarios siempre se asocian de derecha a izquierda ( delete ++*p es delete (++(*p))) y los operadores de postfix unarios siempre se asocian de izquierda a -derecho ( a[1][2]++ es ((a[1])[2])++ ). Tenga en cuenta que la asociatividad es significativa para los operadores de acceso de miembros, aunque estén agrupados con operadores de postfix unarios: a.b++ se analiza (ab)++ y no a.(b++) .

La prioridad del operador no se ve afectada por la sobrecarga del operador. Por ejemplo, std::cout << a ? b : c; std::cout << a ? b : c; analiza como (std::cout << a) ? b : c; (std::cout << a) ? b : c; porque la precedencia del desplazamiento aritmético a la izquierda es mayor que el operador condicional.