49

I saw this code:

if (cond) {
    perror("an error occurred"), exit(1);
}

Why would you do that? Why not just:

if (cond) {
    perror("an error occurred");
    exit(1);
}
Boann
  • 48,794
  • 16
  • 117
  • 146
Kam
  • 5,878
  • 10
  • 53
  • 97
  • 3
    The comma operator is useless outside of expression SFINAE. – Rapptz Jul 27 '13 at 22:16
  • 17
    A comma operator is sometimes useful in contexts such as the body of a macro where you want to combine multiple operations into a single statement. Otherwise, it can be useful when incrementing two variables in a loop, or in a few other stylized places. Generally, though, it is to be avoided; a semi-colon is better than a comma. See also questions such as [Comma operator precedence while used with `?:` operator](http://stackoverflow.com/questions/16854007/comma-operator-precedence-while-used-with-operator) for examples of the confusion caused by the comma operator. – Jonathan Leffler Jul 27 '13 at 22:34
  • 3
    @JonathanLeffler `,` we also frequently use in for loops – Grijesh Chauhan Jul 28 '13 at 06:11
  • 1
    I thought I covered comma in loops with 'when incrementing two variables in a loop'; I didn't specifically mention initializing two variables in a loop, but I hoped that was implicitly covered (and I didn't have all that much space left in the comment). I note that one usage that doesn't work is `if (check_for_error()) print_error("bust"), return -1;` — which is a pity, but it's perfectly kosher for the standard to reject it (`return` doesn't return a value to the function it is written in, unlike calling functions, etc.) – Jonathan Leffler Jul 28 '13 at 06:24
  • @JonathanLeffler You should be able to do `return (print_error("bust"), -1)`. Not sure if we can omit the `()` (too lazy to look for the precedence table). – glglgl Aug 05 '13 at 07:15
  • @glglgl: yes, you can use the comma operator in the return expression (and the parentheses aren't necessary). However, it disturbs the code more than inserting a function call on a line preceding the `return` would. – Jonathan Leffler Aug 05 '13 at 07:46
  • 1
    possible duplicate of [What does the ',' operator do in C?](http://stackoverflow.com/questions/52550/what-does-the-operator-do-in-c) – Sergey K. Aug 28 '13 at 08:01

9 Answers9

65

In your example it serves no reason at all. It is on occasion useful when written as

if(cond)
  perror("an error occured"), exit(1) ;

-- then you don't need curly braces. But it's an invitation to disaster.

The comma operator is to put two or more expressions in a position where the reference only allows one. In your case, there is no need to use it; in other cases, such as in a while loop, it may be useful:

while (a = b, c < d)
  ...

where the actual "evaluation" of the while loop is governed solely on the last expression.

Jongware
  • 22,200
  • 8
  • 54
  • 100
  • 47
    In other words, the comma operator is mainly useful for obfuscation. – James Kanze Jul 27 '13 at 22:41
  • 10
    A comma operator combines two or more *expressions*, not statements. – Keith Thompson Jul 27 '13 at 23:19
  • 2
    @JamesKanze: Or macros - `#define show_error(str, code) perror(str), exit(code)` and then `show_error` behaves as function `if (cond) show_error("an error occured", 1);`. Also see Grijesh Chauhan answer. – Maciej Piechotka Jul 28 '13 at 07:01
  • @MaciejPiechotka The macro you show certainly doesn't behave as a function. In C++, at least (and in modern C), it should be written as an inline function, to ensure that it _does_ behave as a function. (In older C, it would be written `do { if ( cond ) { char const* p = str; perror( p ); exit( code ); } while ( 0 )`, in order to behave like a function. No comma operator there, either. – James Kanze Jul 28 '13 at 15:05
  • I was in doubt of a "real world" use case where the while loop could actually benefit of the comma operator more than with other possible constructs, even though this answer is nice. Link to [another nice answer](https://stackoverflow.com/a/52615/7372188) that gives this "real world" usage, just in case someone else is also looking for it. – dandev486 Aug 14 '22 at 15:08
21

Legitimate cases of the comma operator are rare, but they do exist. One example is when you want to have something happen inside of a conditional evaluation. For instance:

std::wstring example;
auto it = example.begin();
while (it = std::find(it, example.end(), L'\\'), it != example.end())
{
    // Do something to each backslash in `example`
}

It can also be used in places where you can only place a single expression, but want two things to happen. For instance, the following loop increments x and decrements y in the for loop's third component:

int x = 0;
int y = some_number;
for(; x < y; ++x, --y)
{
    // Do something which uses a converging x and y
}

Don't go looking for uses of it, but if it is appropriate, don't be afraid to use it, and don't be thrown for a loop if you see someone else using it. If you have two things which have no reason not to be separate statements, make them separate statements instead of using the comma operator.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • 1
    Billy, isn't the result of an assignment its latest value? Since you are re-evaluating `it` immediately after assigning, you can add the test without the comma operator. (It's a valid example, though.) – Jongware Jul 27 '13 at 22:23
  • 1
    @Jongware: Yes, in that specific case you could do that. Personally, I find the comma more readable than putting assignments in conditions (because of the potential for confusing `=` vs. `==`). But that is a style choice. – Billy ONeal Jul 27 '13 at 22:24
  • Ta. I usually try to avoid *both* constructions for readability's sake ;-) – Jongware Jul 27 '13 at 22:25
  • @Jongware: Yes. About the only time I like seeing this is in a loop, if it allows one to express the entire iteration pattern of the loop inside the first line of the loop. (That way you don't have to search the whole loop body and try to follow a more complex iteration pattern) – Billy ONeal Jul 27 '13 at 22:27
  • 1
    @BillyONeal Either way, you have a side effect in a condition, which is something to be avoided. It's a good example of where the comma operator makes it easier to write poor code. – James Kanze Jul 27 '13 at 22:58
  • @James: That depends on what "poor code" is. I find side effects in a condition, if doing so allows a loop statement to encapsulate the iteration pattern of the loop, result in something that is more readable. Of course, it is better to avoid the need for things like this, but design is all about choosing the "least bad" answer for a given situation. – Billy ONeal Jul 28 '13 at 01:08
  • Poor code is code that is more difficult to read than is necessary. Side effects in a condition make the code more difficult to read, so should be avoided in good code. (But the issue is about avoiding the "least bad". Most of the time, however, this will lead to use of the comma operator in the incrementation clause of a `for`, _not_ in the condition.) – James Kanze Jul 28 '13 at 15:01
  • @James Yes, side effects in conditions are more difficult to read. Loops which aren't described by their statement are also more difficult to read. Personally, I like the side effect condition more, because it localizes the "badness" to one statement. – Billy ONeal Jul 28 '13 at 17:49
6

The main use of the comma operator is obfuscation; it permits doing two things where the reader only expects one. One of the most frequent uses—adding side effects to a condition, falls under this category. There are a few cases which might be considered valid, however:

The one which was used to present it in K&R: incrementing two variables in a for loop. In modern code, this might occur in a function like std::transform, or std::copy, where an output iterator is incremented symultaneously with the input iterator. (More often, of course, these functions will contain a while loop, with the incrementations in separate statements at the end of the loop. In such cases, there's no point in using a comma rather than two statements.)

Another case which comes to mind is data validation of input parameters in an initializer list:

MyClass::MyClass( T const& param )
    : member( (validate( param ), param) )
{
}

(This assumes that validate( param ) will throw an exception if something is wrong.) This use isn't particularly attractive, especially as it needs the extra parentheses, but there aren't many alternatives.

Finally, I've sometimes seen the convention:

ScopedLock( myMutex ), protectedFunction();

, which avoids having to invent a name for the ScopedLock. To tell the truth, I don't like it, but I have seen it used, and the alternative of adding extra braces to ensure that the ScopedLock is immediately destructed isn't very pretty either.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 7
    "*The main use of the comma operator is obfuscation*" -- I don't think that's true. It certainly *can* be used that way, but there are plenty of legitimate non-obfuscated uses. (If you limit your observations to code written by beginners, you're probably right.) – Keith Thompson Jul 28 '13 at 01:01
  • @KeithThompson The main use I've seen has been obfuscation. I do give several examples where its use might be justified, however. Where the alternatives aren't really any clearer than using the comma operator. But it has been much abused, and most of the examples posted in the other examples are abuse. (Interestingly, it is more often abused in C++ than in C. In C++, you can overload it, and _all_ of the uses I've seen of overloading are abuse.) – James Kanze Jul 28 '13 at 14:58
6

This can be better understood by taking some examples:

First: Consider an expression:

   x = ++j;

But for time being, if we need to assign a temporarily debug value, then we can write.

   x = DEBUG_VALUE, ++j; 

Second:
Comma , operators are frequently used in for() -loop e.g.:

for(i = 0, j = 10; i < N; j--, i++) 
 //      ^                   ^     here we can't use ;  

Third:
One more example(actually one may find doing this interesting):

if (x = 16 / 4), if remainder is zero then print  x = x - 1;  
if (x = 16 / 5), if remainder is zero then print  x = x + 1;

It can also be done in a single step;

  if(x = n / d, n % d) // == x = n / d; if(n % d)
    printf("Remainder not zero, x + 1 = %d", (x + 1));
  else
    printf("Remainder is zero,  x - 1 = %d", (x - 1));

PS: It may also be interesting to know that sometimes it is disastrous to use , operator. For example in the question Strtok usage, code not working, by mistake, OP forgot to write name of the function and instead of writing tokens = strtok(NULL, ",'");, he wrote tokens = (NULL, ",'"); and he was not getting compilation error --but its a valid expression that tokens = ",'"; caused an infinite loop in his program.

Community
  • 1
  • 1
Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
5

The comma operator allows grouping expression where one is expected.

For example it can be useful in some case :

// In a loop
while ( a--, a < d ) ...

But in you case there is no reason to use it. It will be confusing... that's it...

In your case, it is just to avoid curly braces :

if(cond)
    perror("an error occurred"), exit(1);

// =>
if (cond)
{
    perror("an error occurred");
    exit(1);
}

A link to a comma operator documentation.

SCFrench
  • 8,244
  • 2
  • 31
  • 61
Pierre Fourgeaud
  • 14,290
  • 1
  • 38
  • 62
  • You second example (`int a = 4, b = 5;`) is not assignment but initialization; the operator is not a comma operator (for all there is a comma separating the two definitions). – Jonathan Leffler Jul 27 '13 at 22:25
2

There appear to be few practical uses of operator,().

Bjarne Stroustrup, The Design and Evolution of C++

Most of the oft usage of comma can be found out in the wikipedia article Comma_operator#Uses.

One interesting usage I have found out when using the boost::assign, where it had judiciously overloaded the operator to make it behave as a comma separated list of values which can be pushed to the end of a vector object

#include <boost/assign/std/vector.hpp> // for 'operator+=()'
using namespace std;
using namespace boost::assign; // bring 'operator+=()' into scope

{
    vector<int> values;  
    values += 1,2,3,4,5,6,7,8,9; // insert values at the end of the container
}

Unfortunately, the above usage which was popular for prototyping would now look archaic once compilers start supporting Uniform Initialization

So that leaves us back to

There appear to be few practical uses of operator,().

Bjarne Stroustrup, The Design and Evolution of C++

Abhijit
  • 62,056
  • 18
  • 131
  • 204
1

In your case, the comma operator is useless since it could have been used to avoid curly braces, but it's not the case since the writer has already put them. Therefore it's useless and may be confusing.

Iosif Murariu
  • 2,019
  • 1
  • 21
  • 27
1

It could be useful for the itinerary operator if you want to execute two or more instructions when the condition is true or false. but keep in mind that the return value will be the most right expression due to the comma operator left to right evalutaion rule (I mean inside the parentheses)

For instance:

a<b?(x=5,b=6,d=i):exit(1);
0

The boost::assign overloads the comma operator heavily to achieve this kind of syntax:

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;
Sergey K.
  • 24,894
  • 13
  • 106
  • 174