4

I recently discovered that this is valid C++ syntax:

int bar = 0;
for(int foo = 0; bar = 0, foo != 10; foo++)
{
    //some code
}

I had never seen a comma used as a separator of two conditionals before, so I looked up how it works.

I found that when separating a list of conditions with commas, all of them get executed, but only the last one is used as the condition. So for example:

while(function1(), function2(), function3())
{
    //code
} 

Here, function1, function2, and function3 will all be run each time through the loop. However, only function3's return value will be used to determine whether or not to keep looping.

My question is this: Are there any situations in which this is the best thing to do?

To me, this makes much more sense:

while(function3())
{
    function1();
    function2();

    //some code
} 

When would it be appropriate to instead use commas to separate conditions?

user3330644
  • 413
  • 1
  • 6
  • 16
  • This is more a matter of coding style. – 101010 Aug 11 '14 at 18:29
  • In the condition, it doesn't make much sense. And I agree, for readability purposes, it can be better to put the first two function calls inside the loop body. This pattern is more often useful in the increment part of a `for` loop when you need to manipulate more than one variable (as in `for (j = i + N; i < j; i++, j--)` etc.) – The Paramagnetic Croissant Aug 11 '14 at 18:30
  • is that a typo in the example? also, the presented examples aren't equivalent. perhaps you should start from the basics, looks like you're asking questions which aren't appropriate for your level. – Karoly Horvath Aug 11 '14 at 18:34
  • 1
    I think this one answers your question. http://stackoverflow.com/a/1618867/3927604 – kanatti Aug 11 '14 at 18:37
  • Related: [Uses of C comma operator](http://stackoverflow.com/questions/1613230/uses-of-c-comma-operator) – Shafik Yaghmour Aug 11 '14 at 18:37
  • 2
    That isn't two logical "conditionals", its an assignment and a single conditional. The result of the first expression is meaningless (but does have side effects, obviously). – WhozCraig Aug 11 '14 at 18:38
  • 2
    This one answers your question. http://stackoverflow.com/a/1618867/3927604 – kanatti Aug 11 '14 at 18:38
  • 2
    `while( f3() ) { f1(); f2() }` does NOT have the same semantics as `while( f1(), f2(), f3() ) { }`. – Rob K Aug 11 '14 at 18:44
  • C++ inherited this beast from C, but there is really not much of a legitimate use. The code lends to wrong interpretation upon casual inspection, which is **not** a good thing. In your example, the casual reader may overlook that `bar` is reset to zero _every iteration_ (and the coder may make false assumptions about the order of execution, as you did...). – Damon Aug 11 '14 at 18:44
  • It is clearly appropriate when you're entering an obfuscated code contest. – Hot Licks Aug 11 '14 at 19:00

3 Answers3

8

It depends on what you mean by condition: a predicate specifically or just any expression that is appropriate where a condition is expected by the surrounding context.

From the former point of view, I'd say that it is never appropriate to use comma to separate conditions. A condition is, by natural definition, an expression that is valuable specifically for its boolean result and not for its side effects (if any). Comma operator always ignores the results of all of its operands with the exception of the very last one. That immediately means that there's no meaningful way to specify a condition anywhere except for the very last position in the comma-separated sequence.

The first and intermediate operands of comma operator are supposed to be expressions whose whole purpose is in their side-effects. It is hardly justified to refer to such expressions as conditions since their results are ignored.

From the latter point of view, it might indeed make sense to include such side-effect producing sub-expressions in contexts where conditions are expected (like the middle segment of the for cycle header), but in many cases they lead to rather ugly code.

One semi-viable example I can come up with might look as follows

for (ListItem* ptr = list_header; 
     assert(ptr != NULL), ptr->key != target_key;
     ptr = ptr->next);

which is supposed to emphasize the fact that the list absolutely must contain the target_key (i.e. the cycle must never fall off end of the list). But I would personally express it differently.

The transformation in your example with while(function1(), function2(), function3()) is not equivalent, since in general case function3() might depend on the side effects of function1(), function2(). This would mean that the ordering of these calls cannot be changed, which is probably the main reason they all were stuffed into the while condition in the first place. In such situations it might make sense to express it as while(function1(), function2(), function3()), but I'd rather transform it into

do 
{
  function1();
  function2();
  if (!function3())
    break;
  ...
} while (true);
AnT
  • 302,239
  • 39
  • 506
  • 752
  • @Jarod42: No, if won't fail. `assert` is required to be an expression, and it is required to convert into a valid expression regardless of whether `NDEBUG` is defined. A typical `assert` definition for `NDEBUG` mode is `((void)0)`. – AnT Aug 11 '14 at 20:35
  • One common usage is to increment two iterators at the same time in a for loop. (though that's in a different part of the for-signature – Mooing Duck Aug 11 '14 at 20:54
2

It is useful if you want to write obfuscated code. Take your first example:

for(int foo = 0; bar = 0, foo != 10; foo++)

I'm certain most people will misread it as either:

for(int foo = 0, bar = 0; foo != 10; foo++)

or:

for(int foo = 0; bar == 0, foo != 10; foo++)

(and the second version is of course nonsense, as the bar == 0 comparison has absolutely no effect).

1000 Bites
  • 940
  • 8
  • 9
1

The calls of functions sometimes are needed because their calls have side effects and the last function call in the condition depends on these side effects.

In any case each situation should be considered separatly that to confirm that such a construction is optimal.

Vlad from Moscow
  • 265,791
  • 20
  • 170
  • 303