60

We can write an if statement as

if (a == 5, b == 6, ... , thisMustBeTrue)

and only the last condition should be satisfiable to enter the if body.

Why is it allowed?

BSMP
  • 4,596
  • 8
  • 33
  • 44
Abdul Rehman
  • 1,687
  • 18
  • 31
  • 3
    You can execute commands, like checking something or initializing. But I confess, I would never use it as it is not readable. In for loops it is more common to increase/decrease multiple variables. – usr1234567 Nov 06 '15 at 07:53
  • The advantage lies in the realm of cryptography (i.e., one programmer making her code hard to read to other programmers). – 101010 Nov 06 '15 at 07:54
  • 1
    The language allows a lot of things that are known to cause undefined behavior. At least these are harmless. – R Sahu Nov 06 '15 at 07:54
  • @RSahu Not really, if you consider time lost due to bugs. – Neil Kirk Nov 06 '15 at 07:55
  • @RSahu you run the risk of being hit by your coworkers. You should get hit as a matter of fact – bolov Nov 06 '15 at 07:55
  • the advantage is that... nope... no advantage here... move along – bolov Nov 06 '15 at 07:56
  • To my understanding, only the last expression matters as `a == 5` and `b == 6` do not contribute to the result of the comma-operator and have no side-effect (unless the `==` operator is overloaded in some weird way). – Codor Nov 06 '15 at 07:58
  • 8
    It depends on the types of `a` and `b`. For user defined types, `operator==` can be overloaded and have side effects. One such side effect could be to deposit a certain amount of money in your bank account. With that in mind, you could consider it advantageous to invoke that operator many times. – juanchopanza Nov 06 '15 at 07:59
  • 1
    @RSahu this one doesn't cause UB though – M.M Nov 06 '15 at 08:05
  • 1
    One application is silencing dump warnings http://stackoverflow.com/a/1947233/57428 – sharptooth Nov 06 '15 at 08:42
  • 16
    @juanchopanza: If you are going the _operator overloading for obfuscation sake_ way, they you can also override the `operator,()` and have extra fun! – rodrigo Nov 06 '15 at 08:49
  • 1
    @sharptooth: You mean, one application is to work around a long-standing bug in a certain compiler. ;-) – DevSolar Nov 06 '15 at 09:00
  • 1
    @DevSolar Oh no, it's not a bug, it's "by design", it warns you about... I dunno what. – sharptooth Nov 06 '15 at 09:03
  • Possible duplicate of [What does the comma operator \`,\` do in C?](http://stackoverflow.com/questions/52550/what-does-the-comma-operator-do-in-c) – Martin G Nov 06 '15 at 17:59
  • Perhaps better title "what is the use of ..." – Walter Nov 06 '15 at 19:51

7 Answers7

69

Changing your example slightly, suppose it was this

if ( a = f(5), b = f(6), ... , thisMustBeTrue(a, b) )

(note the = instead of ==). In this case the commas guarantee a left to right order of evaluation. In constrast, with this

if ( thisMustBeTrue(f(5), f(6)) )

you don't know if f(5) is called before or after f(6).

More formally, commas allow you to write a sequence of expressions (a,b,c) in the same way you can use ; to write a sequence of statements a; b; c;. And just as a ; creates a sequence point (end of full expression) so too does a comma. Only sequence points govern the order of evaluation, see this post.

But of course, in this case, you'd actually write this

a = f(5);
b = f(6);    
if ( thisMustBeTrue(a, b) )

So when is a comma separated sequence of expressions preferrable to a ; separated sequence of statements? Almost never I would say. Perhaps in a macro when you want the right-hand side replacement to be a single expression.

Community
  • 1
  • 1
Jon Jagger
  • 728
  • 7
  • 13
  • 1
    Nice example, but I'd rather use a while - loop header for demonstration. for the `if` case, I don't see a reason, why I wouldn't just write those statements in front of the if statement. – MikeMB Nov 06 '15 at 22:02
45

In short: Although it is legal to do so, it usually doesn't make sense to use the comma operator in the condition part of an if or while statement (EDIT: Although the latter might sometimes be helpful as user5534870 explains in his answer).

A more elaborate explanation: Aside from its syntactic function (e.g. separating elements in initializer lists, variable declarations or function calls/declarations), in C and C++, the , can also be a normal operator just like e.g. +, and so it can be used everywhere, where an expression is allowed (in C++ you can even overload it).
The difference to most other operators is that - although both sides get evaluated - it doesn't combine the outputs of the left and right expressions in any way, but just returns the right one.
It was introduced, because someone (probably Dennis Ritchie) decided for some reason that C required a syntax to write two (or more) unrelated expressions at a position, where you ordinarily only could write a single expression.

Now, the condition of an if statement is (among others) such a place and consequently, you can also use the , operator there - whether it makes sense to do so or not is an entirely different question! In particular - and different from e.g. function calls or variable declarations - the comma has no special meaning there, so it does, what it always does: It evaluates the expressions to the left and right, but only returns the result of the right one, which is then used by the if statement.

The only two points I can think of right now, where using the (non-overloaded) ,-operator makes sense are:

  1. If you want to increment multiple iterators in the head of a for loop:

    for ( ... ; ... ; ++i1, ++i2){
        *i2=*i1;
    }
    
  2. If you want to evaluate more than one expression in a C++11 constexpr function.

To repeat this once more: Using the comma operator in an if or while statement - in the way you showed it in your example - isn't something sensible to do. It is just another example where the language syntaxes of C and C++ allow you to write code, that doesn't behave the way that one - on first glance - would expect it to. There are many more....

Community
  • 1
  • 1
MikeMB
  • 20,029
  • 9
  • 57
  • 102
  • :-) I am clear about for loop , I am specifically asking about **if** and **while** statement. – Abdul Rehman Nov 06 '15 at 08:31
  • 1
    @AbdulRehman: Well in that case the anser probably is: because it if and while statements require an expression that evaluates to true and (,) is an expression. – MikeMB Nov 06 '15 at 08:33
  • If it is the case why only last condition of an expression matters ? – Abdul Rehman Nov 06 '15 at 08:35
  • 12
    @Abdul: Well, because you have to pick one and someone decided that it should be the last. You have to understand, that the comma operator doesn't have any special behavior in an if statement compared to using it at other positions in your code. – MikeMB Nov 06 '15 at 08:41
  • @AbdulRehman: In principle yes, but I'm not enough of a language laywer to tell you what it is in c/c++-standardese terms. – MikeMB Nov 06 '15 at 17:07
  • Can you clearify for me, if there is a reason to use the comma expression in a constexpr where you cannot simply use 2+ constexprs instead? – BeyelerStudios Nov 06 '15 at 17:12
  • @BeyelerStudios: In c++11 you can only write one statement inside a constexpr function. This is no longer the case in c++14. – MikeMB Nov 06 '15 at 17:13
  • @BeyelerStudios: Come to think of it. As constexpr functions can't have sideffects (I believe), there is probalby no use in having two expressions, if you can't use both results. – MikeMB Nov 06 '15 at 17:16
  • "C/C++" is not a language, and even if you mean "C and C++" parts of this answer (e.g. the comma operator can be overloaded) are only true for C++. – You Nov 06 '15 at 17:17
  • @You: I didn't want to imply that, but most of what I wrote - especially the part that directly answers the question about its use with boolean expressions in `if` and `while` statements - is true for both languages. Anyway, I edited my answer to make a clearer distinction between C and C++ - I hope it is acceptable now. – MikeMB Nov 06 '15 at 17:25
  • @Abdul Rehman: The comma operator is different than comma as punctuation. –  Nov 06 '15 at 17:51
  • `, `is also useful in `decltype` and in parameter pack expansion tricks. – Yakk - Adam Nevraumont Nov 06 '15 at 18:24
  • @Yakk: Sounds interesting - can you give an example? – MikeMB Nov 06 '15 at 19:25
  • 1
    @MikeMB in `decltype` you can use it as a simple SFINAE. `decltype( test_expression, void(), result_we_want)` will test the first expression (determine if it is valid), and if it is, will return `result_we_want` type. Parameter pack expansion tricks -- `using discard=int[]; (void)discard{ 0, ( (std::cout << ts << '\n'), void(), 0 )... };` will expand the statement `std::cout << ts << '\n'` for each element in the `ts...` pack (in order) and discard the result. (not all `,` are comma operator in this case, but some are) – Yakk - Adam Nevraumont Nov 06 '15 at 19:28
  • The opposite side of the `for` loop also seems reasonable/useful: `for (int i = 0, n = someList.count(); i < n; ++i)`, where `someList.count()` is a non-trivial operation. If not writing for performance, why write C or C++ at all? – KRyan Nov 06 '15 at 20:17
  • @KRyan: Yes, but in that case, the `,` wouldn't be a comma-**operator**. Thats why I took it out of the example after another user (I forgot his name) mentioned this in the comments. – MikeMB Nov 06 '15 at 20:57
  • @MikeMB Why would it not be? And if it is not an operator in this instance, what *is* it? – KRyan Nov 06 '15 at 21:03
  • @KRyan: It's just a syntax element (or whatever the official name is) - just the way it's not a comma operator, when you declare mutliple variables in other parts of your program or if you pass multiple arguments to a function. Interestingly, if you would not define new variables, but only assign values to existing ones, it would be an operator. – MikeMB Nov 06 '15 at 21:17
  • @MikeMB Huh, I would have assumed it was treated the same in an initializer as in any other statement. I mean, yes, you have to stick to one type and don't repeat the type-name, which I suppose is difference enough. – KRyan Nov 06 '15 at 21:29
  • @KRyan: You can try it yourself: Just overload the `,`-operator for a user defined type (e.g. Print some message) and try both variants. – MikeMB Nov 06 '15 at 21:44
  • @Whoever doesn't like the answer: Would you please comment on the downvote? – MikeMB Nov 07 '15 at 10:01
  • @Yakk that SFINAE example might just be the single best reason to ever willingly use the comma operator – BeyelerStudios Nov 07 '15 at 14:22
  • You'd have to ask Kernighan or Ritchie to be sure, but I would bet a cookie that the "increment multiple iterators in one for-loop head" use case was the original reason the comma operator was added to the language, and that it's allowed everywhere because of C's general "let the programmer do clever things" design philosophy and/or because limiting where it could be used would have added code to the original C compiler (which just barely fit in the original UNIX mini). – zwol Nov 07 '15 at 23:46
15

For an if statement, there is no real point in putting something into a comma expression rather than outside.

For a while statement, putting a comma expression to the condition executes the first part either when entering the loop, or when looping. That cannot easily be replicated without code duplication.

So how about a s do...while statement? There we have only to worry about the looping itself, right? It turns out that not even here a comma expression can be safely replace by moving the first part into the loop.

For one thing, destructors for variables in the loop body will not have already been run then which might make a difference. For another, any continue statement inside the loop will reach the first part of the comma expression only when it indeed is in the condition rather than in the loop body.

user5534870
  • 159
  • 2
10

There is no advantage: the comma operator is simply an expression with type of the last expression in its expression list and an if statement evaluates a boolean expression.

if(<expr>) { ... }
 with type of <expr> boolean

It's a weird operator true, but there's no magic to it - except that it confuses lists of expressions with argument lists in function calls.

foo(<args>)
 with <args> := [<expr>[, <expr>]*]

Note that in the argument list, comma binds stronger to separating arguments.

BeyelerStudios
  • 4,243
  • 19
  • 38
  • they could simply use assignment expression rather than including expression list in c++ GRAMMAR of If statement. – Abdul Rehman Nov 06 '15 at 08:00
  • @AbdulRehman you could write `if ( foo(), a == b )` to call a function and then do the test – M.M Nov 06 '15 at 08:06
  • yes, but it is different than expression in c++ grammar, expression is a list of assignment expressions, while assignment expression is the one without commas in it. – Abdul Rehman Nov 06 '15 at 08:10
  • 3
    I think I we both agree that the comma expression is unnecessary ;) in case of the for-loop, the grammar for initialise and increment could have been simply a list of statements instead of an expression and we wouldn't have this confusion... – BeyelerStudios Nov 06 '15 at 08:16
  • @BeyelerStudios could you clear my point, is there any advantage if we have overloaded some relational operators or equality operators ? – Abdul Rehman Nov 06 '15 at 08:28
  • @AbdulRehman of course: imagine implementing or any other mathematical library. – BeyelerStudios Nov 06 '15 at 08:41
  • Unnecessary? On the contrary, as I recall in my Answer. As for general use of the operator, see Boost.Assign. – JDługosz Nov 07 '15 at 01:23
  • @JDługosz This might sound biased - and it is - but macros and domain specific language features inside C++ is the worst justification for this operator. – BeyelerStudios Nov 07 '15 at 13:52
  • @BeyelerStudios good point; the increment expression's value is not used, but is there only for sode effects. In a language that makes a distinction between expressions and statements, they clearly used the wrong one for the job here. – JDługosz Nov 08 '15 at 15:08
7

What follows is a bit of a stretch, depending on how devious you might wish to be.

Consider the situation where a function returns a value by modifying a parameter passed by reference or via a pointer (maybe from a badly designed library, or to ensure that this value is not ignored by not being assigned after returning, whatever).

void calculateValue(FooType &result) {/*...*/}

Then how do you use conditional statements that depend on result?

You could declare the variable that will be modified, then check it with an if:

FooType result;
calculateValue(result);
if (result.isBared()) {
    //...
}

This could be shortened to

FooType result;
if (calculateValue(result) , result.isBared()) {
    //...
}

Which is not really worth while. However, for while loops there could be some small advantages. If calculateValue should/can be called until the result is no longer bar'd, we'd have something like:

FooType result;
calculateValue(result);  //[1] Duplicated code, see [2]
while (result.isBared()) {
    //... possibly many lines
    //separating the two places where result is modified and tested

    //How do you prevent someone coming after you and adds a `continue`
    //here which prevents result to be updated in the and of the loop?

    calculateValue(result); //[2] Duplicated code, see [1]
}

and could be condensed to:

FooType result;
while (calculateValue(result) , result.isBared()) {
    //all your (possibly numerous) code lines go here
}

This way the code to update result is in only one place, and is near the line where its conditions are checked.

maybe unrelated: Another reason why variables could be updated via parameter passing is that the function needs to return the error code in addition to modify/return the calculated value. In this case:

ErrorType fallibleCalculation(FooType &result) {/*...*/}

then

FooType result;
ErrorType error;

while (error = fallibleCalculation(result) , (Success==error && result.isBared())) {
    //...
}

but as noted in the comments, you can do this without the comma too:

FooType result;
ErrorType error;

while (Success == fallibleCalculation(result) && result.isBared()) {
    //...
}
frozenkoi
  • 3,228
  • 22
  • 33
  • 1
    @AbdulRehman: Yes, duplication of code is bad. Especially the code way down at the end of the while loop is disconnected from it purpose, and you could very well forget to update it when for some reason the first instance is updated. Note that the "disconnected" argument is the _sole reason_ why the third (increment) component of a `for(..;..;..)` clause exists, and I've never heard anybody complain that **that** is useless. – Marc van Leeuwen Nov 07 '15 at 08:31
  • no need for comma: `while(Success == fallibleCalculation(result) && result.isBared()) { ... }` and for void functions (in this case bad api in the first place) use a `for` to counter the disconnect argument for `while` – BeyelerStudios Nov 07 '15 at 14:10
  • Instead: Consider lambda. – Yakk - Adam Nevraumont Nov 07 '15 at 15:09
  • @BeyelerStudios how would you use for statement? You'd still have to use something like `for (calc(result) ; result.isBared() ; calc(result)) {...}` or did you have something else in mind? – frozenkoi Nov 09 '15 at 18:14
  • @frozenkoi no, that's it. – BeyelerStudios Nov 10 '15 at 07:11
4

None whatsoever. The comparisons on a in that code are completely redundant.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 7
    I dunno; there could be a `void operator==(int rhs) { std::cout << rhs; }`. This is a code base where people are using `,` in an `if` statement, I wouldn't rule it out. – Yakk - Adam Nevraumont Nov 06 '15 at 18:26
2

My question is what is the advantage of commas in if or while statement? Why is it allowed ?

It exists because statements and expressions are different things in C. A compound expression is a construct that is understood from theory (and some other languages) and would be missing without having added it in the form of the comma. Its use in the for statement was the original justification of why they needed it.

But, by making the language more complete from a sound theoretical point of view, it later finds uses that nobody planned. The early C++ was a translator that generated C as its output, and having a sequential expression was absolutely essential in allowing inline functions to really generate "in line" logic in the C code.

That includes any place the expression appears, including the condition of an if statement.

Similarly, it has been used in "interesting" macros. And as much as C++ did away with macros by providing inline functions, as late as up-to-x11 compilers found the Boost FOREACH range loop (eventually, an emulation of the feature added to the language in x11) very handy, and that was a devilishly clever set of macros that involved the comma operator.

(Hmm, the current version expands into multiple statements using chained if/else, rather than cramming it all into a single while.)

Now, there is another way to put any statement into an expression (lambdas), so future crazy business of macros that emulate even newer language features or domain-specific embedded languages might not need to use that anymore.


So, don't write code like that. Unless it's clear and indeed simpler than writing helper functions or splitting into multiple statements.

But it may be just the thing for a macro that you want to easily use in one place and that place is inside the parens of an if or while. That could be justified in a domain-specific language hosted inside the C++ source code, or a language emulation feature like (perhaps) an alternative to exception handling used in an embedded real-time system.

In short, it doesn't have a normal good usage. But it's there for completeness and you never know when someone will find it useful.

JDługosz
  • 5,592
  • 3
  • 24
  • 45