13

I've recently (only on SO actually) run into uses of the C/C++ comma operator. From what I can tell, it creates a sequence point on the line between the left and right hand side operators so that you have a predictable (defined) order of evaluation.

I'm a little confused about why this would be provided in the language as it seems like a patch that can be applied to code that shouldn't work in the first place. I find it hard to imagine a place it could be used that wasn't overly complex (and in need of refactoring).

Can someone explain the purpose of this language feature and where it may be used in real code (within reason), if ever?

John Humphreys
  • 37,047
  • 37
  • 155
  • 255
  • Here's a clever use: http://stackoverflow.com/questions/7905905/howto-check-a-type-for-the-existance-of-parameterless-operator/7906060#7906060 – R. Martinho Fernandes Nov 21 '11 at 23:10
  • 6
    @R.MartinhoFernandes: Clever? I think you are missing the big picture. There's nothing clever in "advanced" C++ metaprogramming, just sado-masochism. – 6502 Nov 21 '11 at 23:25

10 Answers10

21

It can be useful in the condition of while() loops:

while (update_thing(&foo), foo != 0) {
    /* ... */
}

This avoids having to duplicate the update_thing() line while still maintaining the exit condition within the while() controlling expression, where you expect to find it. It also plays nicely with continue;.

It's also useful in writing complex macros that evaluate to a value.

caf
  • 233,326
  • 40
  • 323
  • 462
  • Thanks everyone :) It's funny, I've actually used it in loops before without thinking but it never occurred to me that it was an operator until I asked this question. (I know, it's obvious so I feel a little silly! :p) – John Humphreys Nov 22 '11 at 13:37
  • Similarly, I've seen `while (update_thing(y), test_thing(y))`. It allows you to separate the update and the test into two separate functions for better readability, and is still understandable at the loop -- while (oh wait let's update this thing, ok now--) this thing tests true, do this stuff. – Nic Aug 31 '18 at 22:24
9

The comma operator just separates expressions, so you can do multiple things instead of just one where only a single expression is required. It lets you do things like

             (x)              (y)
for (int i = 0, j = 0; ...; ++i, ++j)

Note that x is not the comma operator but y is.

You really don't have to think about it. It has some more arcane uses, but I don't believe they're ever absolutely necessary, so they're just curiosities.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
7

Within for loop constructs it can make sense. Though I generally find them harder to read in this instance.

It's also really handy for angering your coworkers and people on SO.

bool guess() {
  return true, false;
}
Tom Kerr
  • 10,444
  • 2
  • 30
  • 46
  • 4
    A comma in variable declarations is *not* a use of the comma *operator*, which produces values. – lhf Nov 21 '11 at 23:44
5

Playing Devil's Advocate, it might be reasonable to reverse the question:

Is it good practice to always use the semi-colon terminator?

Some points:

  • Replacing most semi-colons with commas would immediately make the structure of most C and C++ code clearer, and would eliminate some common errors.
  • This is more in the flavor of functional programming as opposed to imperative.
  • Javascript's 'automatic semicolon insertion' is one of its controversial syntactic features.

Whether this practice would increase 'common errors' is unknown, because nobody does this.

But of course if you did do this, you would likely annoy your fellow programmers, and become a pariah on SO.

Edit: See AndreyT's excellent 2009 answer to Uses of C comma operator. And Joel 2008 also talks a bit about the two parallel syntactic categories in C#/C/C++.

As a simple example, the structure of while (foo) a, b, c; is clear, but while (foo) a; b; c; is misleading in the absence of indentation or braces, or both.

Edit #2: As AndreyT states:

[The] C language (as well as C++) is historically a mix of two completely different programming styles, which one can refer to as "statement programming" and "expression programming".

But his assertion that "in practice statement programming produces much more readable code" [emphasis added] is patently false. Using his example, in your opinion, which of the following two lines is more readable?

a = rand(), ++a, b = rand(), c = a + b / 2, d = a < c - 5 ? a : b;
a = rand(); ++a; b = rand(); c = a + b / 2; if (a < c - 5) d = a; else d = b; 

Answer: They are both unreadable. It is the white space which gives the readability--hurray for Python!. The first is shorter. But the semi-colon version does have more pixels of black space, or green space if you have a Hazeltine terminal--which may be the real issue here?

Community
  • 1
  • 1
Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77
  • It is always interesting to consider the contrary positions but that said I'd like to see more evidence for your points as in most cases the semantic difference between , and ; is quite small. Except that for (;;) a, b, c is quite different to for (;;) a; b; c; – Bowie Owens Nov 22 '11 at 03:43
  • @Bowie: I don't have any evidence, as I admit in the answer. But your two examples are good: the first `for (;;) a, b, c;` is clear, but the second `for (;;) a; b; c;` is misleading in the absense of indentation or of braces or of both. – Joseph Quinsey Nov 22 '11 at 04:21
  • 1
    Now that the `Occupy Wall Street` movement seems to be fading, perhaps we can turn our attention back to the vital issue of commas? – Joseph Quinsey Dec 03 '11 at 05:27
2

Everyone is saying that it is often used in a for loop, and that's true. However, I find it's more useful in the condition statement of the for loop. For example:

for (int x; x=get_x(), x!=sentinel; )
{
    // use x
}

Rewriting this without the comma operator would require doing at least one of a few things that I'm not entirely comfortable with, such as declaring x outside the scope where it's used, or special casing the first call to get_x().

I'm also plotting ways I can utilize it with C++11 constexpr functions, since I guess they can only consist of single statements.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
1

I think the only common example is the for loop:

for (int i = 0, j = 3; i < 10 ; ++i, ++j)

As mentioned in the c-faq:

Once in a while, you find yourself in a situation in which C expects a single expression, but you have two things you want to say. The most common (and in fact the only common) example is in a for loop, specifically the first and third controlling expressions.

MByD
  • 135,866
  • 28
  • 264
  • 277
  • 2
    In the first part (in your example) it's not a comma operator. – 6502 Nov 21 '11 at 23:14
  • 3
    @MByD: That's a comma, but it's not using the comma operator. The comma is allowed where declaring several variables. The part on the right _is_ the commma operator though. – Mooing Duck Nov 21 '11 at 23:26
  • Mine was just a remark as the citation was about first and third parts. Probably, given that citation, it would have been better leaving declaration of `i` and `j` outside the for (as it would be necessary in `C`) leaving just a double initialization inside. – 6502 Nov 21 '11 at 23:29
1

The only reasonable use I can think of is in the for construct

for (int count=0, bit=1; count<10; count=count+1, bit=bit<<1)
{
    ...
}

as it allows increment of multiple variables at the same time, still keeping the for construct structure (easy to read and understand for a trained eye).

In other cases I agree it's sort of a bad hack...

6502
  • 112,025
  • 15
  • 165
  • 265
1

The comma operator is useful for putting sequence in places where you can't insert a block of code. As pointed out this is handy in writing compact and readable loops. Additionally, it is useful in macro definitions. The following macro increments the number of warnings and if a boolean variable is set will also show the warning.

#define WARN if (++nwarnings, show_warnings) std::cerr

So that you may write (example 1):

if (warning_condition)
    WARN << "some warning message.\n";

The comma operator is effectively a poor mans lambda function.

Bowie Owens
  • 2,798
  • 23
  • 20
1

I also use the comma operator to glue together related operations:

void superclass::insert(item i) {
  add(i), numInQ++, numLeft--;
}
perreal
  • 94,503
  • 21
  • 155
  • 181
  • Would it be any different if `,` would be replaced by `;` in your example? It looks slightly different (very slightly, though) but what does "to glue together" mean in that context? – TobiMcNamobi May 27 '15 at 09:06
  • it wouldn't make a difference here. But would here: `while (1) ++i, ++j`. Glue together means logically grouping these statements so that it is clear these operations are tightly related. – perreal May 27 '15 at 09:13
0

Though posted a few months after C++11 was ratified, I don't see any answers here pertaining to constexpr functions. This answer to a not-entirely-related question references a discussion on the comma operator and its usefulness in constant expressions, where the new constexpr keyword was mentioned specifically.

While C++14 did relax some of the restrictions on constexpr functions, it's still useful to note that the comma operator can grant you predictably ordered operations within a constexpr function, such as (from the aforementioned discussion):

template<typename T>
constexpr T my_array<T>::at(size_type n)
{
    return (n < size() || throw "n too large"), (*this)[n];
}

Or even something like:

constexpr MyConstexprObject& operator+=(int value)
{
    return (m_value += value), *this;
}

Whether this is useful is entirely up to the implementation, but these are just two quick examples of how the comma operator might be applied in a constexpr function.

monkey0506
  • 2,489
  • 1
  • 21
  • 27