Why are Postfix ++/-- categorized as primary Operators in C#?
Asked Answered
S

3

14

Currently I'm teaching a class of C++ programmers the basics of the C# language. As we discussed the topic operators I used C# standard categories of primary, unary etc. operators.

One of the attendees felt puzzled, because in the C# standard the "postfix ++/--" have been put in the category of primary operators rather than the "prefix ++/--". Her rationale behind this confusion was, that she would rather implement the C++ operator "postfix ++/--" in terms of the operator "prefix ++/--". In other words she would rather count the operator "prefix ++/--" as a primary operator. - I understand her point, but I can't give to her a rationale behind that. OK the operators "postfix ++/--" have a higher precedence than "prefix ++/--", but is this the only rationale behind that?

The spec mentioned it in section "14.2.1 Operator precedence and associativity".

So my very neutral question: Why are Postfix ++/-- categorized as primary Operators in C#? Is there a deeper truth in it?

Sebastian answered 13/8, 2011 at 8:26 Comment(8)
Even the C# spec doesn't mention the category of "primary operators" anywhere...Dedradedric
I haven't so far heard about a distinction between "primary" and "secondary" operators. Could you explain very briefly, or point me to further information?Upswell
@Dedradedric and stakx: See "14.2.1 Operator precedence and associativity" in the ECMA paper.Sebastian
In operator overloading in C#, the ++ operator simply returns the value "x+1" [i.e. the value that is to be assigned to the variable, and used for the value of the prefix operator in an expression]. The rest of the semantics are implemented by the compiler at the call site (via reference or valuetype assignment). This is very different from C++, and is this way for the same reason that assignment operators cannot be overloaded.Provocative
I know, how operator overloading in C# works. I wanted to know the rationale behind the categories.Sebastian
@Nico: Ah, I was searching for the entire phrase rather than just the word "primary". In any case, the spec doesn't explain what primary operators are either.Dedradedric
I think it's an arbitrary decision. One of them has to have higher precedence than the other, if x+++y is to be well defined. I think Jon's answer addresses the confusing part in the middle of the question where you refer to implementing one in terms of the other (which can't be done in C#, there's only a single operator overload available).Harlotry
Apple's Swift programming language guide does explicitly suggest to use prefix increment/decrement by default, unless the specific behavior of the postfix variants is required. (This could be understood like Apple counts prefix increment/decrement as "primary operators".)Sebastian
F
2

Since the ECMA standard itself does not define what a 'Primary' operator is, other than order of precedence (i.e. coming before 'Unary') there can be no other significance. The choice of words was probably bad.

Take into account that in many C-link languages, postfix operators tend to create a temporary variable where the expression's intermediate result is stored (see: "Prefer prefix operators over postfix" at Semicolon). Thus, they are fundamentally different from the prefix version.

Nonetheless, quickly checking how Mono and Visual Studio compile for-loops using the postfix and prefix forms, I saw that the IL code produced is identical. Only if you use the postfix/prefix expression's value does it translate to different IL (only affecting where the 'dup' instruction in placed), at least with those implementations mentioned.

Ferino answered 13/8, 2011 at 13:19 Comment(1)
Hm, strange thing. The "prefer prefix over postfix, if you only need the side effect"-rule is well known to me as a C++ programmer (I also use that rule in C#, Java and Objective-C... you name it). In C++ the optimization is generally only allowed for intrinsic types for the postfix operator, that's the rationale behind the "preference rule".Sebastian
Q
2

EDIT: Okay, now I'm back home, I've removed most of the confusing parts...

I don't know why x++ is classified as a primary expression but ++x isn't; although I doubt it makes much difference in terms of the code you would write. Ditto precedence. I wonder whether the postfix is deemed primary as it's used more commonly? The annotated C# specs don't have any annotations around this, by the way, in either the ECMA edition or the Microsoft C# 4 editions. (I can't immediately find my C# 3 edition to check.)

However, in terms of implementation, I would think of ++ as a sort of pseudo-operator which is used by both prefix and postfix expressions. In particular, when you overload the ++ operator, that overload is used for both postfix and prefix increment. This is unlike C++, as stakx pointed out in a comment.

One thing to note is that while a post-increment/post-decrement expression has to have a primary expression as an operand, a pre-increment/pre-decrement expression only has to have a unary expression as an operand. In both cases the operand has to be classified as a variable, property access or indexer access though, so I'm not sure what practical difference that makes, if any.

EDIT: Just to give another bit of commentary, even though it seems arbitrary, I agree it does seem odd when the spec states:

Primary expressions include the simplest forms of expressions

But the list of steps for pre-increment is shorter/simpler than list of steps for post-increment (as it doesn't include the "save the value" step).

Quickie answered 13/8, 2011 at 8:37 Comment(16)
This C++ FAQ might be relevant to your last statement: One can overload the prefix and postfix form of the ++ operator separately.Upswell
++x++ is no more legal than ++1. The ++ operator returns a value rather a variable/reference. Since you cannot assign to a value (part of the spec of the ++ operator) both expressions are meaningless.Congregationalism
@Dunes: Excellent, I'll remove that bit.Quickie
Colleagues, I guess you didn't read my question carefully. I know, how operator overloading in C# works (I'm not a fond of operator overloading, but I teach it, though). I wanted to know the rationale behind the categories in the spec.Sebastian
@Nico: Yes, I read the question carefully - but your attendee was talking about implementing one operator in terms of the other, which I don't think is appropriate - and should be viewed in the same terms of overloading, where there's one "increment" operator which can be used by either postfix or prefix. Implementing either in terms of the other would be a mistake IMO. That's why overloading is relevant when answering your question.Quickie
Hi Jon, it is commonly accepted idiom in C++ to implement ++() in terms of ++(int). (Doesn't a C# compiler automatically generate extra operators from the user defined ones? The C++ idiom is not different from that, but you have to do it manually.)Sebastian
@Nico: It's not clear to me what you mean, I'm afraid. But I would try to avoid applying C++ idioms to C#, or making any assumptions about how the C# compiler behaves based on C++.Quickie
@Downvoters: I'm assuming Nico's one downvoter based on thinking I didn't read his question correctly... would the other downvoter like to say which part of my answer they disagree with?Quickie
Well, it is ok to define postfix in terms of prefix in C++! Also the accepted idiom in C++ is to express operator+ with operator+= (depicted by Scott Meyers or Herb Sutter, don't know right now). - On the other hand, e.g. the C# compiler allows the application of compound assignment operators automatically as well, if you implemented the basic operators (if you implemented +, you'll gain +=).Sebastian
No, the initial down vote was based on your answer "I don't know it", and this is not acceptable. I like you as an author very very much, but you can not do such a thing! - I hope you understand my point.Sebastian
@Nico: Again, I would try to avoid comparisons with C++, other than to highlight differences. I certainly wouldn't infer C# compiler implementation based on C++ compiler implementation. The compound assignment operators are defined in terms of the non-compound operators, which is consistent with defining prefix and postfix increment in terms of the single "increment" operator, rather than defining one in terms of the other. Both are logically built on the result of that increment operator which doesn't talk about assigning back at all.Quickie
@Nico: You mean it's unacceptable for you for an answer to include "I don't know the answer to one aspect of your question, but here's a useful way of addressing the other aspect of your question" (implementing one in terms of the other)? You're not going to stop me from doing that - I still feel I've given a useful answer. In particular, as well as saying I don't know, I've given a suggestion as to the possible reason, suggested that it's actually irrelevant, and given two research sources which don't offer the information, saving other people from checking them. How is that unhelpful?Quickie
Jon, please let us stop the discussion here. I think it is not ok to say "I can't answer it, but I have something to say on the rest of your text." - That is ok for a comment, but not for an answer! I separated my question clearly from the rest of the text, and only wanted to give the attendee's C++-side rationale behind her confusion in the textual body. I don't want to stop you (gosh no!)! But I have a right to act upon my perception, and nobody will stop me from that as well!Sebastian
@Nico: You think it's not okay, I think it's okay... in particular, it says how I would have replied to that attendee. If you would rather my answer didn't exist (which is usually the test I apply before downvoting) that's fine. I really don't think it would be better as a comment - it's too long, and it addresses significant aspects of the question IMO. But of course you're welcome to disagree. I'd love to hear from the other downvoter too...Quickie
In fact I told her that overloading the prefix/postfix operators is a completely different thing in C#, and I explained it at the same level you did some paragraphs above, I told them: "Let loose your C++-view!". But the implement-operator-A-in term terms-of-operator-B is absolutely ok in C++, and for me (I'm also a C++ teacher) there is no sense to doubt the sense of this at least in C++. C# is a different beast, and made it very clear to the attendees, I hope.Sebastian
@Nico: Then that sounds absolutely fine to me :)Quickie
F
2

Since the ECMA standard itself does not define what a 'Primary' operator is, other than order of precedence (i.e. coming before 'Unary') there can be no other significance. The choice of words was probably bad.

Take into account that in many C-link languages, postfix operators tend to create a temporary variable where the expression's intermediate result is stored (see: "Prefer prefix operators over postfix" at Semicolon). Thus, they are fundamentally different from the prefix version.

Nonetheless, quickly checking how Mono and Visual Studio compile for-loops using the postfix and prefix forms, I saw that the IL code produced is identical. Only if you use the postfix/prefix expression's value does it translate to different IL (only affecting where the 'dup' instruction in placed), at least with those implementations mentioned.

Ferino answered 13/8, 2011 at 13:19 Comment(1)
Hm, strange thing. The "prefer prefix over postfix, if you only need the side effect"-rule is well known to me as a C++ programmer (I also use that rule in C#, Java and Objective-C... you name it). In C++ the optimization is generally only allowed for intrinsic types for the postfix operator, that's the rationale behind the "preference rule".Sebastian
G
1

the difference is that a[i++] will access the element indexed i.

a[++i] will access teh element indexed i+1.

In both cases after execution of a[++i/i++]

i will be i+1.

This can make troubles because you can't make assumption on parameters order

function(i++,i++,i++)

will increment i 3 times but you don't know in wich order. if initially i is 4 you can also have function(4,5,6)

but also function(6,5,4) or also function(6,4,5).

and that is still nothing because I used as example native types (for example "int"), things get worse when you have classes.

When overloading the operator result is not changed, what is changed is it's precedence. and this too can cause troubles.

So in one case "++" is applied before returning the reference, in the other case is applied "after" returning the reference. And when you overload it probably is better having it applied before returnin the reference (so ++something is much better than something++ at least from overloading point of view.)

take a generic class with overloaded ++ (of wich we have 2 items, foo and bar)

    foo = bar ++; //is like writing (foo=bar).operator++();

    foo = ++bar; // is like writing foo= (bar.operator++());

and there's much difference. Especially when you just don't assign your reference but do something more complex with it, or internally your object has stuff that has to do with shallow-copies VS deep copies.

Gonyea answered 16/11, 2012 at 14:59 Comment(5)
Most readers of this question will know the difference between the expressions ++i and i++, but this is not the essence of this question. Both operators have different precedences and I only wanted to know, whether this is the only reason having them in different catgories. Some of your statements are not correct:Sebastian
- In fact the order of execution is strictly defined to be l->r in C# (in opposite to C++). So if i = 0 is an int, then following function call in C#: function(i++, i++, i++); will always result in this execution: function(i, i+1, i+2);Sebastian
- Neither in C# nor in C++ programmers can change the precedence of operators, maybe you wanted to express something different. Some of your statements are correct, but have nothing to do with the question.Sebastian
Infact, that was a nice way to consider the difference between C# and C++ and wich reasons was taken into account in desing of the languages. Just an attemp to go a little more beyond the SPEC. Hoped a more philosopical comment :) This is still related to the question since your class is of C++ students isn't it? that's a step back to C++. regards.Gonyea
I see! With your comment the content of your answer makes sense (+1). Would you codify your comment somehow into the answer? - So other readers can follow your argumentation. According philosophy: please mark clearly, which feature is present in which language(undefined order-of-execution in C++, precedence is immutable etc.). RegardsSebastian

© 2022 - 2024 — McMap. All rights reserved.