¿Cómo funciona el operador de coma?

175

¿Cómo funciona el operador de coma en C ++?

Por ejemplo, si lo hago:

a = b, c;  

¿A termina igualando b o c?

(Sí, sé que esto es fácil de probar, solo documente aquí para que alguien encuentre la respuesta rápidamente).

Actualización: esta pregunta ha expuesto un matiz al usar el operador de coma. Solo para documentar esto:

a = b, c;    // a is set to the value of b!

a = (b, c);  // a is set to the value of c!

Esta pregunta en realidad fue inspirada por un error tipográfico en el código. Lo que estaba destinado a ser

a = b;
c = d;

Convertido en

a = b,    //  <-  Note comma typo!
c = d;
Joe Schneider
fuente
Lea más sobre esto aquí. stackoverflow.com/questions/12824378/…
Coding Mash
1
Posible duplicado de ¿Qué hace el operador de coma `,` en C? . Te ganó por un día. Y la respuesta de lillq proporciona una respuesta a la pregunta sobre a = (b, c);.
jww
55
Pero en este caso, ¿ a = b, c = d;realmente funciona igual que lo previsto a = b; c = d;?
Bondolin
@NargothBond No necesariamente. Si by dson evaluaciones de funciones que usan (y modifican) un estado común, el orden de ejecución no se define hasta C++17.
nyronium

Respuestas:

74

Sería igual b.

El operador de coma tiene una precedencia menor que la asignación.

Leon Timmermans
fuente
129

Tenga cuidado al notar que el operador de coma puede estar sobrecargado en C ++. Por lo tanto, el comportamiento real puede ser muy diferente del esperado.

Como ejemplo, Boost.Spirit utiliza el operador de coma con bastante inteligencia para implementar inicializadores de lista para tablas de símbolos. Por lo tanto, hace que la siguiente sintaxis sea posible y significativa:

keywords = "and", "or", "not", "xor";

Tenga en cuenta que debido a la precedencia del operador, el código es (¡intencionalmente!) Idéntico a

(((keywords = "and"), "or"), "not"), "xor";

Es decir, el primer operador llamado es el keywords.operator =("and")que devuelve un objeto proxy en el que operator,se invocan los s restantes :

keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");
Konrad Rudolph
fuente
Umm, no puedes cambiar la precedencia, lo que significa que probablemente deberías poner paréntesis en tu lista.
Jeff Burdges
18
@Jeff Por el contrario. Con un paréntesis alrededor de la lista, esto no funcionaría ya que el compilador solo ve el operador de coma entre dos char[], que no se puede sobrecargar. El código llama intencionalmente primero operator=y luego operator,para cada elemento restante.
Konrad Rudolph el
125

El operador de coma tiene la precedencia más baja de todos los operadores C / C ++. Por lo tanto, siempre es el último en unirse a una expresión, lo que significa esto:

a = b, c;

es equivalente a:

(a = b), c;

Otro hecho interesante es que el operador de coma introduce un punto de secuencia . Esto significa que la expresión:

a+b, c(), d

está garantizado para tener sus tres subexpresiones ( a + b , c () y d ) evaluadas en orden. Esto es significativo si tienen efectos secundarios. Normalmente, los compiladores pueden evaluar subexpresiones en el orden que consideren adecuado; por ejemplo, en una llamada de función:

someFunc(arg1, arg2, arg3)

Los argumentos pueden ser evaluados en un orden arbitrario. Tenga en cuenta que las comas en la llamada a la función no son operadores; Son separadores.

efotinis
fuente
15
Cabe señalar que ,tiene tan baja prioridad, que incluso va a la zaga en sí ;) ... Es decir: por comas como- operador tiene una prioridad más baja que comas como- separador . Entonces, si desea usar un operador de coma como operador dentro de un argumento de una sola función, asignación de variable u otra lista separada por comas , entonces debe usar paréntesis, por ejemplo:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
underscore_d
68

El operador de coma:

  • tiene la precedencia más baja
  • es asociativo a la izquierda

Se define una versión predeterminada del operador de coma para todos los tipos (incorporada y personalizada), y funciona de la siguiente manera: dado exprA , exprB:

  • exprA es evaluado
  • el resultado de exprAes ignorado
  • exprB es evaluado
  • el resultado de exprBse devuelve como el resultado de toda la expresión

Con la mayoría de los operadores, el compilador puede elegir el orden de ejecución e incluso se debe omitir la ejecución si no afecta el resultado final (por ejemplo false && foo(), omitirá la llamada foo). Sin embargo, este no es el caso para el operador de coma y los pasos anteriores siempre sucederán * .

En la práctica, el operador de coma predeterminado funciona casi de la misma manera que un punto y coma. La diferencia es que dos expresiones separadas por un punto y coma forman dos declaraciones separadas, mientras que la separación por comas mantiene todo como una sola expresión. Esta es la razón por la cual el operador de coma a veces se usa en los siguientes escenarios:

  • La sintaxis de C requiere una sola expresión , no una declaración. por ejemplo enif( HERE )
  • La sintaxis de C requiere una sola declaración, no más, por ejemplo, en la inicialización del forbuclefor ( HERE ; ; )
  • Cuando desee omitir las llaves y mantener una sola declaración: if (foo) HERE ;(¡no haga eso, es realmente feo!)

Cuando una declaración no es una expresión, el punto y coma no se puede reemplazar por una coma. Por ejemplo, estos no están permitidos:

  • (foo, if (foo) bar)( ifno es una expresión)
  • int x, int y (la declaración de variable no es una expresión)

En su caso tenemos:

  • a=b, c;, equivalente a a=b; c;, suponiendo que asea ​​de tipo que no sobrecargue el operador de coma.
  • a = b, c = d;equivalente a a=b; c=d;, suponiendo que asea ​​de tipo que no sobrecargue el operador de coma.

Tenga en cuenta que no todas las comas son en realidad un operador de coma. Algunas comas que tienen un significado completamente diferente:

  • int a, b; --- la lista de declaración de variables está separada por comas, pero estos no son operadores de coma
  • int a=5, b=3; --- esta también es una lista de declaración de variables separadas por comas
  • foo(x,y)--- lista de argumentos separados por comas. De hecho, ¡ xy yse puede evaluar en cualquier orden!
  • FOO(x,y) --- lista de argumentos macro separados por comas
  • foo<a,b> --- lista de argumentos de plantilla separados por comas
  • int foo(int a, int b) --- lista de parámetros separados por comas
  • Foo::Foo() : a(5), b(3) {} --- lista de inicializadores separados por comas en un constructor de clase

* Esto no es del todo cierto si aplica optimizaciones. Si el compilador reconoce que cierto código no tiene absolutamente ningún impacto en el resto, eliminará las declaraciones innecesarias.

Lectura adicional: http://en.wikipedia.org/wiki/Comma_operator

CygnusX1
fuente
¿Vale la pena señalar que si operator ,está sobrecargado, pierde cualquier garantía de asociatividad (al igual que pierde las propiedades de cortocircuito del operator&&y operator||si están sobrecargados)?
YoungJohn
El operador de coma es asociativo a la izquierda, independientemente de si está sobrecargado o no. Una expresión a, b, csiempre significa (a, b), cy nunca a, (b, c). La última interpretación podría incluso conducir a un error de compilación si los elementos son de diferentes tipos. ¿Qué puede estar buscando es el orden de evaluación de los argumentos? No estoy seguro de eso, pero quizás tenga razón: podría suceder que cse evalúe antes (a, b) incluso si la coma es asociativa a la izquierda.
CygnusX1
1
Solo un pequeño comentario sobre la lista de inicialización separada por comas en un constructor de clases, el orden no está determinado por la posición en la lista. El orden está determinado por la posición de declaración de la clase. Por ejemplo, struct Foo { Foo() : a(5), b(3) {} int b; int a; }evacua b(3)antes a(5). Esto es importante si su lista es de esta manera: Foo() : a(5), b(a) {}. b no se establecerá en 5, sino más bien el valor no inicializado de a, sobre el cual su compilador puede advertir o no.
Jonathan Gawrych
Recientemente me encontré con un operador de coma con dos flotadores, ¿cuál es el punto de evaluar y descartar un número?
Aaron Franke
No creo que nadie pueda responder eso. Tendría que mostrarlo en un contexto. ¿Probablemente una pregunta separada?
CygnusX1
38

El valor de aserá b, pero el valor de la expresión será c. Es decir, en

d = (a = b, c);

a sería igual a b, y dsería igual a c.

MobyDX
fuente
19
Casi correcto. Las declaraciones no tienen valores, las expresiones sí. El valor de esa expresión es c.
Leon Timmermans
¿Por qué se usa esto en lugar de a = b; d = c;?
Aaron Franke
Esto me hizo comprender de qué efectos secundarios hablaban las personas.
cordón el
8

El valor de b se asignará a a. No le pasará nada a c

prakash
fuente
2

El valor de a será igual a b, ya que el operador de coma tiene una precedencia menor que el operador de asignación.

Jason Carreiro
fuente
2

Sí, el operador de coma tiene poca prioridad que el operador de asignación

#include<stdio.h>
int main()
{
          int i;
          i = (1,2,3);
          printf("i:%d\n",i);
          return 0;
}

Salida: i = 3
Porque el operador de coma siempre devuelve el valor más a la derecha.
En caso de operador de coma con operador de asignación:

 int main()
{
      int i;
      i = 1,2,3;
      printf("i:%d\n",i);
      return 0;
}

Salida: i = 1
Como sabemos, el operador de coma tiene menor prioridad que la asignación .....

Roopam
fuente
Entonces, ¿cómo es el segundo ejemplo diferente de solo tener i = 1;en esa línea?
Aaron Franke
-3

Primero lo primero: la coma en realidad no es un operador, para el compilador es solo un token que adquiere un significado en contexto con otros tokens.

¿Qué significa esto y por qué molestarse?

Ejemplo 1:

Para comprender la diferencia entre el significado del mismo token en un contexto diferente, veamos este ejemplo:

class Example {
   Foo<int, char*> ContentA;
}

Por lo general, un principiante C ++ podría pensar que esta expresión podría / sería comparar las cosas, pero es absolutamente incorrecto, el significado de la <, >y ,fichas de depent en el contexto de uso.

La interpretación correcta del ejemplo anterior es, por supuesto, que es una instancia de una plantilla.

Ejemplo 2

Cuando escribimos un bucle típicamente for con más de una variable de inicialización y / o más de una expresión que debe hacerse después de cada iteración del bucle, también usamos una coma:

for(a=5,b=0;a<42;a++,b--)
   ...

El significado de la coma depende del contexto de uso, aquí es el contexto de la forconstrucción.

¿Qué significa realmente una coma en contexto?

Para complicarlo aún más (como siempre en C ++), el operador de coma puede sobrecargarse (gracias a Konrad Rudolph por señalarlo).

Para volver a la pregunta, el Código

a = b, c;

significa para el compilador algo como

(a = b), c;

porque la prioridad del =token / operador es mayor que la prioridad del ,token.

y esto se interpreta en contexto como

a = b;
c;

(tenga en cuenta que la interpretación depende del contexto, aquí no se trata de una llamada a función / método o una instauración de plantilla).

Quonux
fuente
1
claro que sí, tal vez usé la terminología incorrecta (para el lexer es un token, claro)
Quonux
2
Cuando uno trabaja con el operador, (sic), la coma es de hecho un operador.
DragonLord
2
Si bien reconocer si el token de coma dado se reconoce como operador de coma (en oposición a, por ejemplo, el separador de argumentos) puede ser un desafío en sí mismo, esta pregunta es específicamente sobre el operador de coma .
CygnusX1