2

After quite some debugging time I felt silly to discover a goof in my code that boils down to something like this :

int main()
{
    double p1[] = { 1, 2, 3 };
    double p2[] = { 1, 2, 3 };
    int color = 1;
    bool some_condition = true;

    if (some_condition) (p1, p2, color);
}

The (p1, p2, color) expression evaluates to its last operant, but should the compiler protect me in some way ? (Visual Studio told nothing)

And yes you guessed it right, I wanted to call a draw function : Draw(p1, p2, color)

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153

2 Answers2

4

In C++ the expression (p1, p2, color) forces the compiler to interpret the commas inside the parentheses as the sequential-evaluation operator. The sequential-evaluation operator is a binary operator that evaluates its first operand as void and discards the result, it then evaluates the second operand and returns its value and type. Therefore, the expression (p1, p2, color) is going to be evaluated in the following way:

  1. First p1 is evaluated and discarded, then (p2, color) is evaluated and the result (p2, color) is returned.
  2. First p2 is evaluated and discarded, then color is evaluated and the result color is returned.

Thus the statement:

if (some_condition) (p1, p2, color);

Is equivalent to:

if (some_condition) color;

Some compilers might raise a warning because during the evaluation of the expression (p1, p2, color) the evaluations of p1 and p2 will result in unused:

CLANG LIVE DEMO GCC LIVE DEMO (As you already mentioned Visual Studio will raise no warning at all.)

Apart from these warnings the code is legitimate C++ (i.e., C++ syntax isn't violated).

Now, whether the compiler should protect you is debatable. In my humble opinion it should protect you, since such expressions although correct from C++ syntax point of view might result, in very hard to spot bugs (e.g., the case of assingment inside an if expression).

101010
  • 41,839
  • 11
  • 94
  • 168
  • "The compiler will raise a warning" - you mean, "some compilers will raise a warning, if you enable suitable warnings". There's no requirement to diagnose this, and apparently the OPs compiler doesn't (at least with default settings). – Mike Seymour Jul 28 '14 at 13:48
3

It is perfectly valid code, so the compiler does not have to issue a diagnostic although having a diagnostic would be helpful in this case. This is one of the reasons many developers prefer clang since they tend to go above what is required when it comes to diagnostic.

For the standards rules on diagnostic messages, we can go to the draft C++ standard section 1.4 Implementation compliance which says (emphasis mine):

  1. The set of diagnosable rules consists of all syntactic and semantic rules in this International Standard except for those rules containing an explicit notation that “no diagnostic is required” or which are described as resulting in “undefined behavior.”

  2. Although this International Standard states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs. Such requirements have the following meaning:

    • If a program contains no violations of the rules in this International Standard, a conforming implementation shall, within its resource limits, accept and correctly execute2 that program.

    • If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this Standard as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.

    • If a program contains a violation of a rule for which no diagnostic is required, this International Standard places no requirement on implementations with respect to that program.

No syntactic or semantic rules are violated by this program and so no diagnostic is required.

We have the following code:

if (some_condition) (p1, p2, color);
                    ^  ^   ^
                    1  2   3

1 is an expression-statement which is valid in this context for and if statement. We can see this by going to the grammar:

if ( condition ) statement

and:

statement:
  attribute-specifier-seqopt expression-statement

and:

expression-statement:
  expressionopt;

and:

primary-expression:
  ( expression )

Both 2 and 3 are the comma operator which will evaluate the left operand and discard the value and then evaluate the right operand again nothing invalid here.

So what does section 5.18 Comma operator says:

A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded value expression (Clause 5).83

and a discarded value expression is covered in section 5 which says:

In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression.

So since the value of the result of the left hand expression is discarded then we must only care about the side-effect. In your specific case evaluating the variable has no other effect besides generating a value hence the warning, but if you had used a function in their place e.g.:

bool func()
{
    //...
}

and change your code to:

if (some_condition) (func(), func(), func() );

neither clang nor gcc will provide a warning since presumably func will perform some side-effet that you care about.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • But you would accept that a compiler might well go above and beyond and issue a warning at some level? – david.pfx Jul 28 '14 at 14:26
  • @david.pfx of course, I added a minor clarification in my answer. – Shafik Yaghmour Jul 28 '14 at 14:29
  • Can you clarify: S5/11 `an expression only appears for its side-effects ... is called a discarded-value expression.` If there are no side-effects, then what? – david.pfx Jul 28 '14 at 14:42
  • @david.pfx added more details. – Shafik Yaghmour Jul 28 '14 at 15:49
  • See also http://stackoverflow.com/questions/24912609/what-is-the-use-of-a-statement-with-no-effect-in-c/24914158#24914158. MSVC apparently warns if you cast to void. – david.pfx Jul 29 '14 at 02:31
  • @david.pfx which seems like the opposite of what you want to do. – Shafik Yaghmour Jul 29 '14 at 16:04
  • my ideal would be that all compilers warn about all statements that have no effect (including discarded value if no side effects); that all compilers accept a missing argument name as an intentional unused argument; and that no-one ever writes or needs to write a cast to void. World peace would be nice too. – david.pfx Jul 30 '14 at 02:43