5

I understand what this C++ function does, but I don't understand why the return statement is written this way:

int intDivide(int num, int denom){
  return assert(denom!=0), num/denom;
}

There is only one statement here, because there is only one ; but the comma confuses me. Why not write:

int intDivide(int num, int denom){
  assert(denom!=0);
  return num/denom;
}

Aside from "elegance" is there something to be gained in the first version?

What exactly is that comma doing anyway? Does it break a single statement into 2 parts such that essentially the above 2 versions are identical?

Jerome Baldridge
  • 518
  • 2
  • 13
  • I never knew this was valid syntax. It looks like something bizarre that I'd intentionally avoid doing (assuming it works how you expect). – byxor Dec 25 '16 at 13:05
  • 2
    The built in comma is an operator that evaluates each expression and then returns the result of the last one (that is assuming it wasn't overloaded for some expression it evaluates, then all hell can break loose). I'm not sure how portable this is, since it all depends heavily on what the assert macro is defined as. – StoryTeller - Unslander Monica Dec 25 '16 at 13:05
  • 2
    The only legitimate reason to write it like your first snippet I can think of is for C++11 `constexpr`. – T.C. Dec 25 '16 at 13:08
  • 1
    @StoryTeller I can't think of an `assert` definition that can break the first version given the requirement that it expands to an expression of type `void`. – T.C. Dec 25 '16 at 13:11
  • @StoryTeller `assert` is defined by C. You need to check the C standard, not C++. – T.C. Dec 25 '16 at 13:19
  • Nevermind. Had to dig deeper into the C99 standard. **§7.2.1.1 ¶ 2** : *"The assert macro puts diagnostic tests into programs; it expands to a void expression."* Still astonishing though. – StoryTeller - Unslander Monica Dec 25 '16 at 13:25
  • @StoryTeller The [C89 Rationale](https://www.lysator.liu.se/c/rat/d2.html#4-2-1) already says that "`assert` must expand to a void expression". – T.C. Dec 25 '16 at 13:26
  • @T.C. - I'd be more reassured if I had an official C89 standard draft the says that. But whatever. If it's well defined it's well defined. – StoryTeller - Unslander Monica Dec 25 '16 at 13:29
  • 1
    @StoryTeller The C89 draft I can find doesn't explicitly have that quote, but it does depict `void assert(int expression);` and say in the library introduction "Likewise, those function-like macros described in the following sections may be invoked in an expression anywhere a function with a compatible return type could be called." – T.C. Dec 25 '16 at 13:36

3 Answers3

5

Although the code didn't seem to use constexpr, C++11 constexpr functions were constrained to have only one statement which had to be a return statement. To do the non-functional assertion and return a value there would be no other option than using the comma operator. With C++14 this constraint was removed, though.

I could imagine that the function was rewritten from a macro which originally read something like this

#define INT_DIVIDE(nom,denom) (assert(denom != 0), nom/denom)

The built-in comma operator simply sequences two expressions. The result of the expression is the second operand. The two functions are, indeed, equivalent. Note, that the comma operator can be overloaded. If it is, the expressions are not sequenced and the result is whatever the overload defines.

In practice the comma operator sometimes comes in quite handy. For example, it is quite common to use the comma operator when expanding a parameter pack: in some uses each of the expansions is required to produce a value and to avoid void results messing things up, the comma operator can be used to have a value. For example:

template <typename... T>
void g(T const& arg) {
    std::initializer_list<bool>{ (f(arg), true)... };
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
1

This is a sort of 'syntactic sugar', which is expanded on in a similar question.

Basically the e1, e2 means evaluate e1, and then evaluate e2 - and the entire statement is the result of e2. It's a short and obfuscated (in my opinion) way of writing what you suggest. Maybe the writer is cheap on code lines.

Community
  • 1
  • 1
kabanus
  • 24,623
  • 6
  • 41
  • 74
0

From the C++ standard:

5.19 Comma operator [expr.comma]
1 The comma operator groups left-to-right.

expression:  
     assignment-expression  
     expression , assignment-expression  

A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded- value expression (Clause 5).87 Every value computation and side effect associated with the left expression is sequenced before every value computation and side effect associated with the right expression. The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field. If the value of the right operand is a temporary (12.2), the result is that temporary.

Yes, the two versions are identical, except if the comma operator is overloaded, as @StoryTeller commented.

alain
  • 11,939
  • 2
  • 31
  • 51