35

I'm confused about direct assignment and ternary conditional operators precedence:

#include<stdio.h>
int main(void)
{
    int j, k;

    j = k = 0;
    (1 ? j : k) = 1; // first
    printf("%d %d\n", j, k);

    j = k = 0;
    1 ? j : k = 1; // second
    printf("%d %d\n", j, k);
    return 0;
}

I would expect the output to be:

1 0
1 0

But it happens to be:

1 0
0 0

Plus I get this warning:

main.cpp:20: warning: statement has no effect

which is about the line I commented as second.

Since the direct assignment operator has less precedence than the ternary conditional operator, I was expecting lines commented as first and second to be equivalent. But alas it is not the case.

I tried this with g++ --version (Ubuntu 4.4.3-4ubuntu5) 4.4.3

Ari Brodsky
  • 117
  • 3
Anonymous Coward
  • 3,140
  • 22
  • 39
  • 5
    Is C++ a marvel, or a beast? After spending half of my life programming with it I can still be caught not knowing a meaning of simple expressions like this. I had to look this in the standard. – Suma Sep 21 '11 at 12:40

6 Answers6

21

The operator precedence in the C/C++ language in not defined by a table or numbers, but by a grammar. Here is the grammar for conditional operator from C++0x draft chapter 5.16 Conditional operator [expr.cond]:

conditional-expression:
    logical-or-expression
    logical-or-expression ? expression : assignment-expression

The precedence table like this one is therefore correct when you use assignment on the left side of the doublecolon, but not when used on the right side. What is the reason for this asymmetry I have no idea. It may be a historical reason: in C the conditional result was not lvalue, therefore assigning something to it had no sense, and allowing assignment to be accepted without parentheses might seem smart at that time.

Suma
  • 33,181
  • 16
  • 123
  • 191
  • 1
    Why is the linked table (that groups ?: and = together) incorrect on the right side of the colon? – foxcub Sep 11 '12 at 00:53
  • 2
    @foxcub Current version of the table is correct. Assignment has the same priority as conditional in the table and associativity is right to left, resulting in assignment be done correctly before the conditional. The table [was incorrect at the time of the answer](http://en.cppreference.com/mwiki/index.php?title=cpp/language/operator_precedence&oldid=5400), stating higher priority for conditional than for assignment. – Suma Sep 11 '12 at 07:14
  • The C++ syntax is defined as a syntactic grammar, but that grammar is designed to be consistent with a simple precedence table. You say the table is "incorrect when used on the left side of the colon", but by that reasoning the precedence is also incorrect when used on the left side of `]` in an expression such as `a[b+c]`. The claim of a precedence table is that *a sub-expression seen by two operators where they expect an operand is given to the one with higher precedence (and ties are broken by associativity).* This claim is never violated. `?:` and `=` simply have the same precedence. – Museful Nov 25 '12 at 12:35
  • This is not correct. The ternary conditional and the assignment operators have equal precedence and are evaluated right-to-left. Always. – Yves Daoust May 11 '23 at 07:32
14

The second line is equivalent to:

1 ? (j) : (k = 1);

That's the same as:

j;

That's the same as:

;

The key is that the two operands of the ternary conditional operator can be expressions, so operator precedence isn't relevant here. It's simply that the second operand is the assignment expression k = 1.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • *nod* The problem is nothing to do with operator precedence at all -- it's that ?: is a short-circuit operator. – Kaz Dragon Sep 21 '11 at 12:07
  • @KazDragon: I clarified that - the key is that the operands are expressions, so the second operand is the entire assignment expression. (All the standard logical binary operators are short-circuited, by the way. Short-circuiting refers to the evaluation of the conditional, not the resulting statements.) – Kerrek SB Sep 21 '11 at 12:10
  • 7
    You are correct, but the question is: why is it equivalent to 1 ? (j) : (k = 1); and not to (1 ? j : k) = 1, when a conditional is supposed to have higher priority than assignment? – Suma Sep 21 '11 at 12:21
  • @Suma: the "priority" doesn't come into this, because there's only one operator involved, whose operands are expressions. You already quoted the relevant part of the grammar that explains that. – Kerrek SB Sep 21 '11 at 12:24
  • 3
    There are multiple operators involved (assignment is operator as well). You do not explain why assignment expression takes precedence over conditional expression (and commonly used precedence tables do not explain this as well). – Suma Sep 21 '11 at 12:32
  • @Suma: I see. What would you suggest? – Kerrek SB Sep 21 '11 at 12:32
9
(1 ? j : k) = 1;

is equivalent to,

if(true) j = 1;
else k = 1;

And,

1 ? j : k = 1;

is equivalent to,

if(true) j;  // warning: statement has no effect
else k = 1;
iammilind
  • 68,093
  • 33
  • 169
  • 336
3

In the second case,

1 ? j : k = 1;

is evaluated as:

(1) ? (j) : (k = 1);

and since one evaluates to true, the expression evaluates to j which does nothing.

flight
  • 7,162
  • 4
  • 24
  • 31
0

Previous answers explain the results, but they don't clear the doubt. I'll try to give my understanding. If there are any mistakes, please point it out.

Compilers may analyze and generate code as follows:

  1. Parse the plain code to a tree (code plain text -> semantic unit).

    Original code break down to minimal semantic unit and format the tree. In the process, compiler need to know each operator's precedence. And according to C++ operator precedence, we know

    • ?:(conditional operator) has same precedence with =(direct assign operator), precedence = 16
    • in this precedence level(==16), the associativity is Right->Left, so k = 1 higher than ?:.

    So for code 1 ? j : k = 1;, the tree may like this:

                   op(?:)
          /         |          \
         /          |           \
      [condition] [true-branch] [false-branch]
          1           j             op(=)
                                    /  \
                                   k    1
    
  2. Decide the Eval order (generate the intermediate representation)

    To generate runnable code, the compiler needs to know which part should eval(run) firstly. According to the Order of evaluation, we know for operator ?:, it should firstly evaluate [condition], then evaluate according to the condition result. It is straightforward and follows our intuitive.

    So imagine the final running process, 1 is eval to true, and true branch j then evaluated, k = 1 will never run.

So after knowing 1,2, we know the fully reason.

As the cpp document, step1 is in compile-time concept, while step2 is in runtime concept. But from the compiler's view, they are all in compiling process, and the compile-time/runtime concept is just from the language-design view.

0

By the precedence rules of C++ (https://en.cppreference.com/w/cpp/language/operator_precedence),

1 ? j : k = 1;

is evaluated right-to-left, i.e.

1 ? (j) : (k = 1);

[also see note 2 in the link].

This indeed has no effect as only the intermediate expression is performed.

Yves Daoust
  • 672
  • 9