194

How does the comma operator work in C++?

For instance, if I do:

a = b, c;  

Does a end up equaling b or c?

(Yes, I know this is easy to test - just documenting on here for someone to find the answer quickly.)

Update: This question has exposed a nuance when using the comma operator. Just to document this:

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

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

This question was actually inspired by a typo in code. What was intended to be

a = b;
c = d;

Turned into

a = b,    //  <-  Note comma typo!
c = d;
Coding Mash
  • 3,338
  • 5
  • 24
  • 45
Joe Schneider
  • 9,179
  • 7
  • 42
  • 59
  • Read more about it here. http://stackoverflow.com/questions/12824378/how-is-the-comma-operator-being-used-here/12824426#12824426 – Coding Mash Dec 14 '12 at 15:54
  • 1
    Possible duplicate of [What does the comma operator \`,\` do in C?](http://stackoverflow.com/questions/52550/what-does-the-comma-operator-do-in-c). It beat you by one day. And lillq's answer provides an answer to the question about `a = (b, c);`. – jww May 08 '15 at 23:33
  • 5
    But in this case `a = b, c = d;` actually does perform the same as the intended `a = b; c = d;`? – Bondolin Nov 11 '15 at 19:52
  • @NargothBond Not necessarily. If `b` and `d` are function evaluations that use (and modify) a common state, the execution order is not defined until `C++17`. – nyronium Sep 09 '18 at 16:38

9 Answers9

139

The comma operator has the lowest precedence of all C/C++ operators. Therefore it's always the last one to bind to an expression, meaning this:

a = b, c;

is equivalent to:

(a = b), c;

Another interesting fact is that the comma operator introduces a sequence point. This means that the expression:

a+b, c(), d

is guaranteed to have its three subexpressions (a+b, c() and d) evaluated in order. This is significant if they have side-effects. Normally compilers are allowed to evaluate subexpressions in whatever order they find fit; for example, in a function call:

someFunc(arg1, arg2, arg3)

arguments can be evaluated in an arbitrary order. Note that the commas in the function call are not operators; they are separators.

efotinis
  • 14,565
  • 6
  • 31
  • 36
  • 16
    Worth pointing out that `,` has such low precedence, it even lags behind _itself_ ;) ...That is: comma-as-_operator_ has a lower precedence than comma-as-_separator_. So, if you want to use comma-as-_operator_ within a single function argument, variable assignment, or other comma-_separated_ list - then you need to use parentheses, e.g.: `int a = 1, b = 2, weirdVariable = (++a, b), d = 4;` – underscore_d Apr 16 '16 at 21:45
137

Take care to notice that the comma operator may be overloaded in C++. The actual behaviour may thus be very different from the one expected.

As an example, Boost.Spirit uses the comma operator quite cleverly to implement list initializers for symbol tables. Thus, it makes the following syntax possible and meaningful:

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

Notice that due to operator precedence, the code is (intentionally!) identical to

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

That is, the first operator called is keywords.operator =("and") which returns a proxy object on which the remaining operator,s are invoked:

keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Umm, you cannot change the precedence though, meaning you should probably put parentheses around your list. – Jeff Burdges Oct 08 '11 at 18:56
  • 23
    @Jeff On the contrary. With a parenthesis around the list this wouldn’t work since then the compiler just sees the comma operator between two `char[]`, which cannot be overloaded. The code *intentionally* first calls the `operator=` and then subsequently `operator,` for each remaining element. – Konrad Rudolph Oct 09 '11 at 11:04
84

The comma operator:

  • has the lowest precedence
  • is left-associative

A default version of comma operator is defined for all types (built-in and custom), and it works as follows - given exprA , exprB:

  • exprA is evaluated
  • the result of exprA is ignored
  • exprB is evaluated
  • the result of exprB is returned as the result of the whole expression

With most operators, the compiler is allowed to choose the order of execution and it is even required to skip the execution whatsoever if it does not affect the final result (e.g. false && foo() will skip the call to foo). This is however not the case for comma operator and the above steps will always happen*.

In practice, the default comma operator works almost the same way as a semicolon. The difference is that two expressions separated by a semicolon form two separate statements, while comma-separation keeps all as a single expression. This is why comma operator is sometimes used in the following scenarios:

  • C syntax requires an single expression, not a statement. e.g. in if( HERE )
  • C syntax requires a single statement, not more, e.g. in the initialization of the for loop for ( HERE ; ; )
  • When you want to skip curly braces and keep a single statement: if (foo) HERE ; (please don't do that, it's really ugly!)

When a statement is not an expression, semicolon cannot be replaced by a comma. For example these are disallowed:

  • (foo, if (foo) bar) (if is not an expression)
  • int x, int y (variable declaration is not an expression)

In your case we have:

  • a=b, c;, equivalent to a=b; c;, assuming that a is of type that does not overload the comma operator.
  • a = b, c = d; equivalent to a=b; c=d;, assuming that a is of type that does not overload the comma operator.

Do note that not every comma is actually a comma operator. Some commas which have a completely different meaning:

  • int a, b; --- variable declaration list is comma separated, but these are not comma operators
  • int a=5, b=3; --- this is also a comma separated variable declaration list
  • foo(x,y) --- comma-separated argument list. In fact, x and y can be evaluated in any order!
  • FOO(x,y) --- comma-separated macro argument list
  • foo<a,b> --- comma-separated template argument list
  • int foo(int a, int b) --- comma-separated parameter list
  • Foo::Foo() : a(5), b(3) {} --- comma-separated initializer list in a class constructor

* This is not entirely true if you apply optimizations. If the compiler recognizes that certain piece of code has absolutely no impact on the rest, it will remove the unnecessary statements.

Further reading: http://en.wikipedia.org/wiki/Comma_operator

CygnusX1
  • 20,968
  • 5
  • 65
  • 109
  • Is it worth noting that if the `operator ,` is overloaded you lose any guarantees on associativity (just like you lose the short-circuit properties of the `operator&&` and `operator||` if they are overloaded)? – YoungJohn Feb 22 '16 at 16:44
  • Comma-operator is left-associative regardless if it is overloaded or not. An expression `a, b, c` always means `(a, b), c` and never `a, (b, c)`. The latter interpretation could even lead to compile error if elements are of different types.What you may be after is the order of evaluation of the arguments? I am not sure about that, but perhaps you are right: it could happen that `c` is evaluated *before* `(a, b)` even if comma is left-associative. – CygnusX1 Feb 22 '16 at 17:15
  • 3
    Just a slight comment on the comma-separated initialization list in a class constructor, order is _not_ determined by position in the list. Order is determined by the declaration position of the class. E.g. `struct Foo { Foo() : a(5), b(3) {} int b; int a; }` evaulates `b(3)` before `a(5)`. This is important if your list is like so: `Foo() : a(5), b(a) {}`. b won't be set to 5, but rather the uninitialized value of a, which your compiler may or may not warn about. – jgawrych Jul 29 '18 at 02:55
  • I recently came across a comma operator with two floats, what is the point of evaluating and discarding a number? – Aaron Franke Oct 17 '18 at 06:22
  • I don't think anyone can answer that. You would have to show it in a context. Probably a separate question? – CygnusX1 Oct 17 '18 at 12:55
78

It would be equal to b.

The comma operator has a lower precedence than assignment.

msanford
  • 11,803
  • 11
  • 66
  • 93
Leon Timmermans
  • 30,029
  • 2
  • 61
  • 110
40

The value of a will be b, but the value of the expression will be c. That is, in

d = (a = b, c);

a would be equal to b, and d would be equal to c.

H.S.
  • 11,654
  • 2
  • 15
  • 32
MobyDX
  • 1,480
  • 1
  • 12
  • 13
8

b's value will be assigned to a. Nothing will happen to c

prakash
  • 58,901
  • 25
  • 93
  • 115
3

Yes Comma operator has low precedence than Assignment operator

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

Output : i=3
Because comma operator always return rightmost value.
In case of comma operator with Assignment Operator:

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

Ouput: i=1
As we know comma operator has lower precedence than assignment.....

Roopam
  • 270
  • 2
  • 12
2

The value of a will be equal to b, since the comma operator has a lower precedence than the assignment operator.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
-3

First things first: Comma is actually not an operator, for the compiler it is just a token which gets a meaning in context with other tokens.

What does this mean and why bother?

Example 1:

To understand the difference between the meaning of the same token in a different context we take a look at this example:

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

Usually a C++ beginner would think that this expression could/would compare things but it is absolutly wrong, the meaning of the <, > and , tokens depent on the context of use.

The correct interpretation of the example above is of course that it is an instatiation of a template.

Example 2:

When we write a typically for loop with more than one initialisation variable and/or more than one expressions that should be done after each iteration of the loop we use comma too:

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

The meaning of the comma depends on the context of use, here it is the context of the for construction.

What does a comma in context actually mean?

To complicate it even more (as always in C++) the comma operator can itself be overloaded (thanks to Konrad Rudolph for pointing that out).

To come back to the question, the Code

a = b, c;

means for the compiler something like

(a = b), c;

because the priority of the = token/operator is higher than the priority of the , token.

and this is interpreted in context like

a = b;
c;

(note that the interpretation depend on context, here it it neither a function/method call or a template instatiation.)

Community
  • 1
  • 1
Quonux
  • 2,975
  • 1
  • 24
  • 32
  • 1
    sure it is, maybe i used the wrong terminology (for the lexer it is a token, sure) – Quonux May 15 '13 at 06:23
  • 2
    As one works with *operator,* (sic), comma is indeed an operator. – DragonLord Jul 11 '13 at 01:56
  • 2
    While recognizing if given comma token is recognized as comma operator (as opposed to, for example, separator of arguments) may be a challenge on its own, this question is specifically about *comma operator*. – CygnusX1 Oct 05 '13 at 14:08