C++ precedence for unary negation vs prefix decrement - c++

I came across the following when learning C++
int a = 5;
-----a;
The second statement doesn't compile. The statement could either be read as --(--(-a)) or -(--(--a)), since both operators are in the same precedence group. In this case though only the second interpretation (when you DO use brackets) makes sense. Therefore I see no ambiguity.
My question therefore is: why is the unary negation not in a higher precedence group than the prefix decrement?

Related

Operator precedence - increment versus member access

Consider this:
++iterator->some_value
Will the iterator be forwarded before some_value is accessed?
According to cppreference increment and member access both have the same precedence of 2. Does the order in which they are listed matter? Or is this undefined - compiler specific?
Note that preincrement and postincrement have different precedences, so the code snippet you've posted doesn't quite match the explanatory text you've linked.
The preincrement operator has a lower precedence than the member access operator, so ++iterator->some_value is the equivalent of ++(iterator->some_value).
If instead you had iterator->some_value++ where they both had the same precedence, then the left-to-right associativity comes into effect, and it would be processed as (iterator->some_value)++.
The member access operator -> has higher precedence over the prefix increment operator. Thus the expression ++iterator->some_value is grouped as if you had written
++(iterator->some_value)
On the other hand, if your were to write the expression iterator->some_value++ then since both the member access operator -> and the postfix increment operator have the same precedence, so left-to-right associativity will be used, which will make this expression equivalent to writing :
(iterator->some_value)++

Does precedence & associativity group operators or operands?

By reading the book "C++ Primer" and wikipedia, I notice both mentioned that "precedence and associativity defines the grouping of OPERATORS". However, it appears to me the examples they given were showing grouping of OPERANDS. Here I quote:
from "Defined terms # C++ Primer 5th edition":
associativity: Determines how OPERATORS with the same precedence are
grouped.
from Operator_associativity # Wikipedia:
Consider the expression a ~ b ~ c. If the operator ~ has left
associativity, this expression would be interpreted as (a ~ b) ~ c. If
the operator has right associativity, the expression would be
interpreted as a ~ (b ~ c).
But from what I see, the above explanation grouped two OPERANDS (not operators): a and b into (a ~ b), or b and c into (b ~ c). Because I see they parenthesized two operands, but not two operators.
Given that operator and operands are different concepts, does the precedence and associativity rule group operator or operands ?
Thanks in advance.
Precedence and associativity address how a language interprets parenthesis-free expressions involving three or more operands. I'll use the symbols # and # to denote two operators. Consider the expressions
a # b # c,
x # y # z,
d # e # f, and
u # v # w.
Note that the first two expressions involve different operands. The C++ precedence rules determines whether a#b#c is interpreted as meaning (a#b)#c or a#(b#c), and whether x#y#z is interpreted as meaning x#(y#z) or (x#y)#z.
The latter two expressions involve the same operand. Precedence has no bearing here. It's associativity that determines whether d#e#f is interpreted as meaning (d#e)#f or d#(e#f), and whether u#v#w is interpreted as meaning (u#v)#w or u#(v#w).
C++ has a large number of operators. There's an easy way to deal with the plethora of precedences: Use parentheses. My rule is "Everyone knows a*b+c means (a*b)+c. Nobody but a language lawyer knows whether a?b:c=d means (a?b:c)=d or a?b:(c=d). Use parentheses when in doubt."
Note: Apparently even Microsoft and wikipedia don't know the correct answer to "What does a?b:c=d mean?", at least as of June 16, 2014. The precedence tables at wikipedia and Microsoft have the ternary operator separate from the lower precedence assignment operators, which is incorrect. That would mean a?b:c=d needs to be interpreted as (a?b:c)=d, which always assigns the value of d to b or c, depending on whether a is true of false. That is incorrect. The correct interpretation is a?b:(c=d). The precedence tables at cppreference.com and cplusplus.com correctly group the ternary operator with the assignment operators.
There's an even better solution to puzzling out what the standard says: Just use parentheses.
Given that operator and operands are different concepts, does the precedence and associativity rule group operator or operands ?
Both. It groups operands and the corresponding operator(s). In:
(a ~ b) ~ c
you are grouping a and b, which are operands, by the ~ operator. Precedence and associativity are properties only of the operand, though.
The associativity rules of a language specify which operator is evaluated first when two operators with the same precedence are adjacent in an expression.
The precedence rules of a language specify which operator is evaluated first when two operators with different precedence are adjacent in an expression.
Wiki says:
[..] the associativity (or fixity) of an operator is a property that determines how operators of the same precedence are grouped in the absence of parentheses. If an operand is both preceded and followed by operators (for example, "^ 4 ^"), and those operators have equal precedence, then the operand may be used as input to two different operations (i.e. the two operations indicated by the two operators). The choice of which operations to apply the operand to, is determined by the "associativity" of the operators.
This means that operators are grouped among the operands and operands are grouped among the operators.

Explain the difference:

int i=1,2,3,4; // Compile error
// The value of i is 1
int i = (1,2,3,4,5);
// The value of i is 5
What is the difference between these definitions of i in C and how do they work?
Edit: The first one is a compiler error. How does the second work?
= takes precedence over ,1. So the first statement is a declaration and initialisation of i:
int i = 1;
… followed by lots of comma-separated expressions that do nothing.
The second code, on the other hand, consists of one declaration followed by one initialisation expression (the parentheses take precedence so the respective precedence of , and = are no longer relevant).
Then again, that’s purely academic since the first code isn’t valid, neither in C nor in C++. I don’t know which compiler you’re using that it accepts this code. Mine (rightly) complains
error: expected unqualified-id before numeric constant
1 Precedence rules in C++ apply regardless of how an operator is used. = and , in the code of OP do not refer to operator= or operator,. Nevertheless, they are operators as far as C++ is concerned (§2.13 of the standard), and the precedence of the tokens = and , does not depend on their usage – it so happens that , always has a lower precedence than =, regardless of semantics.
You have run into an interesting edge case of the comma operator (,).
Basically, it takes the result of the previous statement and discards it, replacing it with the next statement.
The problem with the first line of code is operator precedence. Because the = operator has greater precedence than the , operator, you get the result of the first statement in the comma chain (1).
Correction (thanks #jrok!) - the first line of code neither compiles, nor is it using the comma as an operator, but instead as an expression separator, which allows you to define multiple variable names of the same type at a time.
In the second one, all of the first values are discarded and you are given the final result in the chain of items (5).
Not sure about C++, but at least for C the first one is invalid syntax so you can't really talk about a declaration since it doesn't compile. The second one is just the comma operator misused, with the result 5.
So, bluntly, the difference is that the first isn't C while the second is.

Are C/C++ operator precedence & associativity rules ever violated?

Are operator precedence & associativity rules ever violated in any C/C++ expression?
If so, can you give an example?
Assume the claims of precedence and associativity rules are:
Each operator has a given precedence level, and each precedence level has a given associativity. If a sub-expression is seen by two operators where they expect an operand, it belongs to the one with higher precedence. Ties are broken by associativity.
Edit: Background
The standard defines C/C++ expressions as a CFG, which is much more flexible than a precedence-based parser. For example, it would have been possible to give binary operators asymmetrical "precedence", which would have rendered any precedence table incorrect. However, it appears to me that the design of the grammar was constrained to uphold simple precedence rules. Here are some alleged "counterexamples" that I have come across:
1) a?b,c:d is not interpreted as (a?b),(c:d)
Some claim that the ?: operator exhibits different precedence towards its middle operand than towards its other operands, because a?b,c:d, for example, is not interpreted as (a?b),(c:d). However, neither b nor c occupies a position in which it appears to ?: as its inner operand. By that reasoning a[b+c] should be interpreted as (a[b)+(c]), which is ludicrous.
2) sizeof(int)*a is interpreted as (sizeof(int))*a rather than sizeof((int)(*a))
... because C disallows an uparenthesized cast as sizeof's operator. However, both of these interpretations conform to precedence rules. The confusion comes from the * operator's ambiguity (Is it the binary or the unary operator?). Precedence tables are not meant to resolve operator ambiguities. They are, after all, not operator-symbol-precedence tables. So the operator precedence rules themselves are intact.
3) a+b=c results in syntax error, not semantic error
a+b=c, according to the standard, is invalid C syntax. If C had had a precedence-based parser, it would only have been caught at the semantic level. In C, it so happens that any expression that is not a unary-expression cannot be l-valued. These semantically doomed LHS expressions therefore do not need to be accommodated syntactically. It makes no difference to the language as a whole, and precedence tables needn't be in the business of predicting the syntacticness/symanticness of the error that is going to result from an expression.
For one example, the usual precedence table says that sizeof and cast expressions have the same precedence. Both the table and the standard say that they associate right-to-left.
This simplification is fine when you're looking at, say, *&foo, which means the same as *(&foo).
It might also suggest to you that sizeof (int) 1 is legal C++ and that it means the same thing as sizeof( (int) 1 ). But it's not legal, because in fact sizeof( type-id ) is a special thing of its own in the grammar. Its existence prevents sizeof (int) 1 from being a sizeof expression whose operand is a cast-expression whose operand is 1.
So I think you could say that the "sizeof ( type-id )" production in the C++ grammar is an exception to what the usual precedence/associativity tables say. They do accurately describe the "sizeof unary-expression" production.
It depends on whether the "rules" are correct. The language definition doesn't talk about precedence, so the precedence tables you see in various places may or may not reflect what the language definition actually requires.
Until someone can find a counterexample, I'm going to put forward this as the default answer:
No, C/C++ precedence and associativity rules are never violated.

Difference between sequence points and operator precedence? 0_o

Let me present a example :
a = ++a;
The above statement is said to have undefined behaviors ( I already read the article on UB on SO)
but according precedence rule operator prefix ++ has higher precedence than assignment operator =
so a should be incremented first then assigned back to a. so every evaluation is known, so why it is UB ?
The important thing to understand here is that operators can produce values and can also have side effects.
For example ++a produces (evaluates to) a + 1, but it also has the side effect of incrementing a. The same goes for a = 5 (evaluates to 5, also sets the value of a to 5).
So what you have here is two side effects which change the value of a, both happening between sequence points (the visible semicolon and the end of the previous statement).
It does not matter that due to operator precedence the order in which the two operators are evaluated is well-defined, because the order in which their side effects are processed is still undefined.
Hence the UB.
Precedence is a consequence of the grammar rules for parsing expressions. The fact that ++ has higher precedence than = only means that ++ binds to its operand "tighter" than =. In fact, in your example, there is only one way to parse the expression because of the order in which the operators appear. In an example such as a = b++ the grammar rules or precedence guarantee that this means the same as a = (b++) and not (a = b)++.
Precedence has very little to do with the order of evaluation of expression or the order in which the side-effects of expressions are applied. (Obviously, if an operator operates on another expression according to the grammar rules - or precedence - then the value of that expression has to be calculated before the operator can be applied but most independent sub-expressions can be calculated in any order and side-effects also processed in any order.)
why it is UB ?
Because it is an attempt to change the variable a two times before one sequence point:
++a
operator=
Sequence point evaluation #6: At the end of an initializer; for example, after the evaluation of 5 in the declaration int a = 5;. from Wikipedia.
You're trying to change the same variable, a, twice. ++a changes it, and assignment (=) changes it. But the sequence point isn't complete until the end of the assignment. So, while it makes complete sense to us - it's not guaranteed by the standard to give the right behavior as the standard says not to change something more than once in a sequence point (to put it simply).
It's kind of subtle, but it could be interpreted as one of the following (and the compiler doesn't know which:
a=(a+1);a++;
a++;a=a;
This is because of some ambiguity in the grammar.