C++: why is iter++->empty() legal? - c++

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.

Related

Using ++ as a prefix to a statement of access through class member not causing an error

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.

Is postfix increment and decrement left to right associative or the contrary?

In "C++ primer 5th edition" on page 228 The table Table 4.4. Operator Precedence shows operators and associativitiy of operands.
What I am confused about is in this table says that Prefix increment/decrement is Right-To-Left associative and also Postfix increment/decrement also Right-To-Left So there's the letter "R" that means right to left. But in www.cppreference.com I see that postfix increment/decrement are Left-To-Right associative.
If someone makes things clear up through giving an example containing Compound expression, then is really appreciated.
The C++ grammar defines a postfix expression like this:
postfix-expression:
primary-expression
...
postfix-expression ++
....
In parsing a ++ ++, a is a primary-expression, so a ++ is a postfix-expression. The final ++ applies to the result of that postfix-expression.
In short, ++ groups left to right.
The same thing applies to all of what we usually think of as postfix operators: they apply to a postfix-expression, so they group left to right.
As mentioned in a comment, going the other way would make ptr[i]++ rather funky.
Looking at a (probably illegal) PDF version of that book that I found online, I suspect the the entries for postfix++ and postfix-- are cut-and-paste typos. Both of those operators are supposedly described on page 147, as are prefix++ and prefix--, but the only discussion there is about prefix++ and prefix--.
There's no book safe from Errata. Each version of a book adds some enhancements and corrects some mistakes. The author is always appreciating Errata reporting. Anyway: The post-fix increment and -decrement are Left-To-Right associative.
int x = 5;
x++;
As you can see from the expression above: the operand xis at the lhs of the operator ++ thus you easily can understand it.
++x;
Now the operand x is on the right thus Pre-increment/decrement is Right-To-Left.
Prefix operators are Right to Left associative:
https://en.cppreference.com/w/cpp/language/operator_precedence
Neither C++ Primer 5th edition from Prata nor from Lippman have any operator priority table on page 228.

How strict C/C++ compilers about operator precedence/evaluation? [duplicate]

This question already has answers here:
order of evaluation of || and && in c
(5 answers)
Closed 9 years ago.
This question is been on my mind for a while so time to let it out and see what what you guys you have to say about it.
In C/C++ the operator precedence is defined by the C specification but as with everything there may be backdoors or unknown / not well known things that the compilers may employ under the name of 'optimization' which will mess up your application in the end.
Take this simple example :
bool CheckStringPtr(const char* textData)
{
return (!textData || textData[0]==(char)0);
}
In this case I test if the pointer is null then I check the first char is zero, essentially this is a test for a zero length string. Logically the 2 operations are exchangeable but if that would happen in some cases it would crash with since it's trying to read a non-existent memory address.
So the question is : is there anything that enforces the order of how operators/functions are executed, I know the safest way is to use 2 IFs below each other but this way should be the same assuming that the evaluation order of the operators never ever change.
So are compilers forced by the C/C++ specification to not change the order of evaluation or are they sometimes allowed to change the order, like it depends on compiler parameters, optimizations especially?
First note that precedence and evaluation order are two different (largely unrelated) concepts.
So are compilers forced by the C/C++ specification to not change the order of evaluation?
The compiler must produce behaviour that is consistent with the behaviour guaranteed by the C language standard. It is free to change e.g. the order of evaluation so long as the overall observed behaviour is unchanged.
Logically the 2 operations are exchangeable but if that would happen in some cases it would crash
|| and && are defined to have short-circuit semantics; they may not be interchanged.
The C and C++ standards explicitly support short-circuit evaluation, and thus require the left-hand operand of the &&, ||, or ? operator to be evaluated before the right-hand side.
Other "sequence points" include the comma operator (not to be confused with commas separating function arguments as in f(a, b)), the end of a statement (;), and between the evaluation of a function's arguments and the call to the function.
But for the most part, order of evaluation (not to be confused with precedence) is implementation defined. So, for example, don't depend on f to be called first in an expression like f(x) + g(y).

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.