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)++
Related
Exercise 4.20 of C++ Primer, 5e asks whether the expression iter++->empty(); is legal. Assume that iter is a vector<string>::iterator.
This expression is legal. I compiled it with gcc, and the answers to another question on Stack Overflow have addressed this much. However, I'm confused as to why it is legal.
This answer to a similar question gives the following as an equivalent pair of expressions:
iter->empty();
iter++;
The operator precedence table in my book lists -> as having higher precedence than the postfix ++ operator. This matches the explicit order of operations in the equivalent code above. However, I am used to seeing operators apply to whatever is right next to them. In the case of ->, I expected the compiler would to apply it to ++ (by itself, without iter) and throw an error. In other words, I tried to parenthesize the original expression as iter(++->empty());, which is obviously illegal.
So, it seems like c++ requires compilers to parse expressions in a more complex way than just parenthesizing based on precedence and associativity. Is that right? If there is an easy way to explain how this actually happens, I would like to know about it.
Per cppreference, ++ and -> have the same precedence and have left to aright associativity. That means that iter++ is executed first, and then ->empty() is applied to the result of iter++, which is just iter (from before the increment) since it is postfix increment.
I am kind of confused right now, I was running the following code:
std::vector<int> test{1, 5, 10};
++test.at(1); // I'm trying to increment that number 5 to six, which works
std::cout << test.at(1) << std::endl; // Prints out 6 to the console
I was expecting it to give me a compiler error because as I had read from about the operator precedence that the . (for member access) and the increment operator (++) have the same precedence, but they read left to right on a statement, from what I understood anyways. So in the code shown above I thought it would have been equal to saying (++test).at(1), which obviously causes a compiler error. Why isn't that the case even though the associativity is left to right, why is it reading it (from what I think) like this... ++(test.at(1))? If they have the same precedence wouldn't it, just like in maths, for example, use the ++ first and then the .?
True, postfix increment (a++) and member access (.) have the same precedence.
But you're using prefix increment (++a).
Consult cppreference's precedence chart.
Indeed, test++.at(i) would error for the reasons you give, though as readers of the code we would not be in any way surprised in that case.
Function calls have a higher operator precedence than unary operators like "++". The function call at() gets resolved first, and so the increment operator takes place on what it returns instead of the container.
https://en.cppreference.com/w/cpp/language/operator_precedence
EDIT: As Asteroids With Wings pointed out in their answer, only the prefix version of "++" has lower precedence than the function call. The postfix "++" is at the same level of precedence.
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?
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.
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.