Why does the postfix increment operator take a dummy parameter?
Asked Answered
F

6

24

Have a look at these function signatures:

 class Number {
 public:
   Number& operator++ ();    // prefix ++
   Number  operator++ (int); // postfix ++
 }; 

Prefix doesn't take any parameter but postfix does. Why? I thought we can recognize them with different return types.

Fula answered 26/8, 2010 at 11:58 Comment(4)
The only time C++ overloads by return type is for the typecast operator.Hornsby
@Mike: I don't believe that overload resolution comes into play in that case.Konya
Good point. I've always thought of it as "overloading the typecast operator", but maybe there's a better term.Hornsby
My impression has always been that there isn't a single typecast operator. Rather, there are many typecast operators; the cast to each target type is its own distinct operator.Potamic
R
12

Prefix and postfix ++ are different operators. With the standard Foo operator symbol(Foo &) style declaration there was no obvious way to distinguish the two. Rather than come up with some new syntax like Foo symbol operator(Foo &) which would make it into a special case unlike all the other operators and likely a bit of a pain to parse, the language designers wanted some other solution.

The solution they chose was somewhat bizarre. They noted that all the other 'postfix' operators (i.e. operators that occurred after one of their operands) were actually infix operators that took two arguments. For example, plain old +, / or >. On this basis the language designers decided that having a random dummy argument would be a good way to distinguish between prefix and postfix ++.

IMHO, it's one of the stranger decisions made as C++ evolved. But there you have it.

And you can't distinguish them based on return type for two reasons.

The first is that functions in C++ cannot be overloaded on return type. You cannot have two functions that have identical names and parameter type lists but different return values.

The second is that method would not be robust or flexible enough to handle all possible implementations of prefix and postfix ++.

For example, you might want a postfix ++ that returned a reference type if the only reason you ever called it was to invoke a side-effect unrelated to the value of the variable you were applying it to. In my opinion, that would be a very bad implementation, but C++ is not about judging what kinds of stupid code you want to write, but about enabling you to write whatever code it is you think appropriate to the situation. And forcing you to use one particular style of return type for prefix ++ and postfix ++ would be contrary to that spirit.

Ricci answered 26/8, 2010 at 12:20 Comment(9)
As a side note, I gleaned this information from "The Design and Evolution of C++" by Bjarne Stroustroup. I would recommend it if you want to really understand why C++ is the way it is. Growing a language by accretion in an environment where backward compatibility is very important leads to a lot of strange things.Ricci
There aren't any really good alternatives, though. You could invent a pseudo-operator (e.g. operator +++) that people will forget or get wrong, or add a keyword (e.g. post operator ++), or abandon using the operators themselves and use special names instead (e.g. Python's __add__ and friends). All look worse than the dummy variable.Hornsby
Yes, operator post_increment would be much easier to read, but then post_increment becomes a reserved word, possibly breaking compilers or other programs which already used that name. Hence Python's "names with leading double underscores are all reserved" convention. See the history of XHTML for an example of why not to cause massive backward compatibility breakage in a new standard.Hornsby
Also, C and C++ have historically had a strong aversion to keywords: there are very few in those languages, and when they add some (e.g. static_cast) there's a lot of pushback, especially from programmers whose native language is not English.Hornsby
@Mike - Yes, exactly, and that's the decision the people designing C++ made as well. It doesn't make it any less strange. Though I do have to agree with @Dennis about readability. Again, preserving backwards compatibility and designing by accretion leads to strange results sometimes.Ricci
@Mike: You don't have to make post_increment a new keyword . If you introduce a new syntax for "explicit" operator names, then you can add a rule like: 'operator' 'explicit' <identifier> (<args>) where identifier must be one of the explicit operator names (like 'post_increment', 'pre_increment' etc.). From a parsing standpoint, they don't have to be reserved words, because they're only "special" in then operator explicit position. This is really easy to implement!Parados
@Luther: it might have repercussions if it is at all common to have those phrases used as macro names. Otherwise, you're right.Konya
@Luther - Yes, that might work, and it would be, IMHO, the best solution.Ricci
Also, once you have a typedef which reads typedef int post; you can write R operator++(post); which i think looks not too bad.Dilution
P
10

You are free to give operator++ any return type you like, so there's not way to distinguish postfix and prefix by that. So the compiler needs some kind of clue.

OTOH, I don't know why this couldn't had been done in syntax only:

//prefix
int& ++operator (); 
//postfix
int& operator++ (); 

After all, mimicking usage in declarations has tradition in C and C++.

P.S. Other posters: This has nothing to do with overloading by return type. postfix and prefix ++/-- are two different names. There is no need to resolve an overload in x++ or ++x, because it's entirely clear which name is meant.

Parados answered 26/8, 2010 at 12:0 Comment(12)
is it, might be they could have done it in the way you suggested :).. so Just for the compiler differentiation they write parameter right? another Question, why they made postfix to accept parameter, why not prefix? they could have interchanged right, any specific difference?Fula
That probably would have required special allowances in the grammar, which is already about as complex as they come. Plus, the next logical step would be to make the other unary operators match the prefix/postfix notation for consistency. In which case, what would T &operator() mean?Konya
@Shadow: The extra parameter for postfix works as a visual reminder that the action happens of the right hand side..Parados
++x and x++ may be different syntactic sugar, but operator++() and operator++(int) look very much like two overloadings of the same function name to me.Engrossing
"...it's entirely clear which name is meant", unless of course the compiler is of the sound opinion that both ++x and x++ mean x.operator++(). There is a need for a resolution, and overload resolution is a perfectly reasonable way to do it. There aren't any other operators in C++ that are differentiated by "fixity" alone, so it would be somewhat extravagant and cluttering to alter the operator syntax for only two special cases. And the int param has no guaranteed value, so I believe the compiler is free to not actually pass anything, thus invoking no extra cost.Tondatone
Overloading couldn't be done as ++operator because then, to be consistent, you'd have to make all the standard unary operators (+, -, !, *, ~) prefix notation, and then *operator would be ambiguous, since int *operator() could look like "functional operator returning pointer to int" instead of "pointer dereference operator returning int". Also, the parser is easier to implement (and better at catching errors) if the keyword operator always comes first.Hornsby
@Mike: the ambiguity is the reason. If ++operator where allowed the lexing would be much more difficult (which in this case spells: orders of magnitude slower).Kabul
@Fabio: How does that affect lexing? operator and ++ are two different tokens. It shouldn't make a difference for the lexer if the come in either order.Parados
@Jon not true. The int parameter is guaranteed to be 0 when invoked by the compiler for processing an expression.Dilution
@Luther Blissett, you mean, function overloading should be differentiated only by the parameter type, return type is optional or not?Fula
@Luther you are probably right, I mixed something up, and thought that the whole Identifier would be treated as a token. Sorry.Kabul
@Johannes Schaub: Thanks, could you point me to a source for that?Tondatone
W
7

Straight from Bjarne's mouth:

This may be both too cute and too subtle, but it works, requires no new syntax, and has a logic to the madness. Other unary operators are prefix and take no arguments when defined as member functions. The "odd" and unused dummy int argument is used to indicate the odd postfix operators. In other words, in the postfix case, ++ comes between the first (real) operand and the second (dummy) argument and is thus postfix.

These explanations are needed because the mechanism is unique and therefore a bit of a wart. Given a choice, I would probably have introduced the prefix and postfix keywords, but that didn't appear feasible at the time. However, the only really important point is that the mechanism works and can be understood and used by the few programmers who really need it.

By the way, in my opinion, only prefix ++ should be overloadable by the programmer, and postfix ++ should be automatically generated by the compiler. Does anyone agree with me?

Wayland answered 26/8, 2010 at 14:48 Comment(3)
Does anyone agree with me? - #include <boost/operators.hpp>Spay
Not the way the language is currently set up, no. I do think it would be neat if, after 0x, we could default operators and let the compiler build them from other operators you do define.Konya
You know I didn't agree with you... But that seems to be the only way we could have avoided this overload resolution problem. I'm just not sure how you'd handle it for objects? Instead of the object the compiler would have to pass a copy of the object, which might get dicey for objects that don't have a copy constructor.Socalled
S
1

You're not allowed to overload functions purely by return type, so a dummy parameter is necessary to differentiate between two identical looking operator++() operators.

Savina answered 26/8, 2010 at 12:0 Comment(0)
G
0

If I had my druthers, postincrement and many sequence-point operators would be split into two or three parts; in the case of postincrement, a statement like "a=(b++ + c++);" would be effectively translated as "a=postinc1(b)+postinc1(c); postinc2(b); postinc2(c);"; the second part of post-increment would be a void function. In actual implementation, the postinc2() calls should often likely occur while some other results are sitting on the evaluation stack; that shouldn't be too hard for a compiler to implement.

In the case of "&&" or "||", the first part of the operator would just operate on the left operand; if it returned non-zero (for &&) or non-zero (for ||), then the second part would operate on both operands.

In the case of "?"/":", the first part of the operator would operate on just the first operand. If it returned non-zero, the second part would operate on the first and second parameters; otherwise the third part would operate on the first and third parameters.

Do any languages do anything like that? It seems odd that C++ allows operators to be redefined in ways that break sequencing behavior, but does not allow them to be redefined in ways that preserve it.

Glycoprotein answered 16/9, 2010 at 23:4 Comment(0)
B
0

The compiler uses the int argument to distinguish between the prefix and postfix increment operators. For implicit calls, the default value is zero so actully it does not make so much difference in functionality of overloaded operator...

Bittern answered 23/11, 2013 at 6:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.