Conditional operator differences between C and C++
Asked Answered
M

2

54

I read somewhere that the ?: operator in C is slightly different in C++, that there's some source code that works differently in both languages. Unfortunately, I can't find the text anywhere. Does anyone know what this difference is?

Makeup answered 4/7, 2009 at 17:41 Comment(0)
G
75

The conditional operator in C++ can return an lvalue, whereas C does not allow for similar functionality. Hence, the following is legal in C++:

(true ? a : b) = 1;

To replicate this in C, you would have to resort to if/else, or deal with references directly:

*(true ? &a : &b) = 1;

Also in C++, ?: and = operators have equal precedence and group right-to-left, such that:

(true ? a = 1 : b = 2);

is valid C++ code, but will throw an error in C without parentheses around the last expression:

(true ? a = 1 : (b = 2));
Grouch answered 4/7, 2009 at 17:55 Comment(3)
In C you could write *(true? &a: &b) = 1 insteadLobo
Just a small question about (condition ? a = 1 : b = 2); -- is this used as a "shortcut" for if-else situations? Would you think that's done often? I tend to consider it misuse :-/Arrington
= in the last part of the ?: operator is not defined in the C grammar, but many compilers parse it as "a ? b = 1 : c = 1" == "(a ? (b = 1) : c) = 1", which will then fail since ?: is never an lvalue in C. See en.cppreference.com/w/c/language/operator_precedenceBowlds
A
24

The principal practical difference is that in C, evaluation of ?: can never result in a l-value where as in C++ it can.

There are other differences in its definition which have few practical consequences. In C++ the first operand is converted to a bool, in C it is compared against 0. This is analagous to the difference in definition of ==, !=, etc. between C and C++.

There are also more complex rules in C++ for deducing the type of a ?: expression based on the types of the 2nd and 3rd operands. This reflects the possibility of user-defined implicit conversions in C++.

Example code. Valid C++; invalid C.

extern int h(int p, int q);

int g(int x)
{
        int a = 3, b = 5;

        (x ? a : b) = 7;

        return h( a, b );
}

gcc generates the error: "error: invalid lvalue in assignment" when compiling as C, but the code compiles without error when compiling as C++.

Edit: Although ?: can't return an l-value in C, perhaps surprisingly the grammar for ?: is:

conditional-expression:
    logical-OR-expression
    logical-OR-expression ? expression : conditional-expression

This means that a ? b : c = d parses as (a ? b : c) = d even though (due to the 'not an l-value' rule) this can't result in a valid expression.

C++ changes the grammar to this:

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

While the extension to allow conditional-expression to be an l-value in some situations would have made a ? b : c = d valid without the grammar change, the new grammar change means that the expression is now valid but with the different meaning of a ? b : (c = d).

Although I don't have any evidence for it, my supposition that as the grammar change couldn't break compatibility with existing C code, it was more likely that the new grammar would produce fewer surprises with expressions such as:

make_zero ? z = 0 : z = 1;
Asymmetric answered 4/7, 2009 at 17:54 Comment(2)
The part of the answer that describes C parse is incorrect. In C grammar the LHS of = operator cannot contain a non-parenthesized ?: operator. The suggested (a ? b : c) = d parse is not really possible. In C grammar a ? b : c = d expression is not parsable at all. Any compiler that interprets it as (a ? b : c) = d is doing it at its own risk. (This is typically the case in hybrid C/C++ compilers that attempt to unify C and C++ grammars under the hood.) Formally, there's no problem in it, since invalid code is still rejected. But "not an lvalue" message is misleading.Ignatia
In C, a ? b : c = d is simply syntactic gibberish, a non-parsable sequence of tokens. It is not an expression. Claiming that it assigns to a non-lvalue is incorrect, since there's no "assignement" here.Ignatia

© 2022 - 2024 — McMap. All rights reserved.