6

In particular, the prefix operators' return-by-reference makes sense to me - it's useful, in case one wants to do further operations on the object.

However, I can't get my head around why the postfix operator was designed to return by value.

Is it solely a convention, or was there a good reason why it was designed this way (like a return-by-value does not make sense for postfix, but makes sense for prefix)?

Can someone explain?

ANSWER

Thanks to the answers below, it appears that the postfix operator doesn't necessarily have to return by value (according to the standards).

However, due to the semantic requirements of the postfix operator (return the original value, but increment the reference to the original value afterwards), in conjunction with the standard requirement of:

Operator overloads are functions, and thus all side effects must take place before the function completes.

as explained clearly by David Rodriguez below, bifurcating the value seems to be a necessary consequence of the semantic requirements.

In this context, since we are returning the other value (not the original reference, since it will have changed by the closing brace of the function), returning the other value by-value seems to make the most sense.

Community
  • 1
  • 1
kfmfe04
  • 14,936
  • 14
  • 74
  • 140
  • 3
    How would you increment it after using it otherwise? – chris Feb 12 '13 at 04:36
  • Technically you could work around it for your own classes if you maintained a variable that told it to increment it on the next use. Seems a bit much to me, though. – chris Feb 12 '13 at 04:38
  • *" I think in most examples, the object still exists."* -- What object? The object that was incremented? Yes, it does, usually. But that's irrelevant since the object that's being incremented is not the object that the postfix operator returns. I'm not sure you're getting this. – Benjamin Lindley Feb 12 '13 at 05:06
  • @BenjaminLindley I understand it is so, *due to the convention of return-by-value*. I'm trying to understand *why* the object **has to be different** (this is almost a meta-question: why was prefix designed as return-by-reference while postfix is return-by-value?). I think http://stackoverflow.com/a/14825705/975129 below is a good explanation. – kfmfe04 Feb 12 '13 at 05:10
  • Returning by value in this case is not really done because of a convention. It is done out of necessity, because of the convention of returning the previously held value. But yes, I agree that answer clearly explains why it is a necessity. – Benjamin Lindley Feb 12 '13 at 05:13
  • Well thought out question. I was curious about the same thing: http://stackoverflow.com/a/36498791/2642059 Sorry it hasn't gotten more positive attention :/ – Jonathan Mee Apr 08 '16 at 14:34

4 Answers4

14

Postfix operators are expressions that yield the original value and then modify the object. At the same time, operator overloads are functions, and thus all side effects must take place before the function completes. The only way of attaining the required semantics is by copying the initial state of the object before applying the change. The original state must be returned by value (if a reference was returned, the evaluation of the expression would yield the state of the object after the function completes, and thus would have the semantics of prefix operators, not postfix ones)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • +1 *At the same time, operator overloads are functions, and thus all side effects must take place before the function completes.* I think that's the crucial convention (hence, no lazy-evaluation is allowed: language/operator semantics would be broken). – kfmfe04 Feb 12 '13 at 05:16
5

It is the convention because a typical implementation of post fix involves creating a temporary object local to the function, while incrementing the originally passed object using the prefix operator and then returning the temporary object. You cannot return a reference to that local object and hence it needs to be returned by value.

You cannot return a reference because the local object is guaranteed to be alive only within the scope of the function and any access to it beyond this scope will result in Undefined Behavior.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • +1 The first paragraph looks like a tautology. However, I think your last paragraph is on the right track - is there a specific example (or something in the C++ spec) which shows an object being reaped by the time a postfix operation is executed. That would definitely be a cause for return-by-value instead of return-by-reference. – kfmfe04 Feb 12 '13 at 04:51
  • 1
    @kfmfe04: then you should read it more carefully. To be more explicit - `X operator++(int) { X x(*this); ++*this; return x; }` / +1 – Tony Delroy Feb 12 '13 at 05:17
  • @kfmfe04: I think you misunderstand that the standard demands that postfix should return by value. No it doesn't. The standard only demands specific observable behaviors and implementations are free to implement it in any way. In this case the expected behavior can be achieved conveniently by returning a vale and most implementations, I fail to see what part of it you cannot understand. – Alok Save Feb 12 '13 at 05:27
  • That's really interesting. (this is not a perfect or even a good implementation - just for the sake of understanding) If I were to return by reference to a static object which held a copy of the original value, then that would be ok (according to the standard)? – kfmfe04 Feb 12 '13 at 05:31
  • @AlokSave 8^P that kind of implementation would be garbage (especially in a threading context) - but that definitely clarifies things - I did not know where the standard stood on this topic. TYVM for taking the time to explain. – kfmfe04 Feb 12 '13 at 05:35
  • @kfmfe04: "this is not a perfect or even a good implementation" - please justify your assertions or people can't educate you. Remember you're the one who needs to ask about this stuff, but your attitude is remarkably judgemental. Re returning statics, consider: `X x1(10), x2(20); const int& i = x1++; x2++;` where `X` is a simple wrapper for numbers. if a single static was shared by all objects of the class, then `i` would see 10 after `x1++` was evaluated, but switch to 20 after `x2++`: instead, it should remember the 10 value indefinitely, which is most easily achieved by return-by-value. – Tony Delroy Feb 12 '13 at 06:45
  • @TonyD aye - there are many reasons (in addition to your example) why it's not a good idea to use statics in this case: I just threw out that static implementation example to clarify the limits of the standard (not limit in a bad sense - just what the standard requires). fwiw, all the negative comments by me, in this thread of comments, were directed toward my use of static in the example, not towards anyone else's contributions; also, there may be due to some confusion due to a *deleted* iter-comment by AlokSave. I hope I didn't cause any misunderstandings. – kfmfe04 Feb 12 '13 at 07:20
  • @kfmfe04: ahhh, I see. "(this is not a perfect..." was intended as a lead in to your question about statics. The capital in "If" made me think it was a new, unrelated sentence, with the "not perfect" bit being a comment on the implementation I illustrated. No worries. – Tony Delroy Feb 12 '13 at 08:25
3

The following code is well-defined in C and C++:

int i = 7;
printf("%i\n", i++ + 2);

This will print 9 to the console, while i will be 8. Guaranteed.

The postfix increment/decrements modify i, but they return the old value of i. The only way to do that with an object is to save the current value in a new value, increment yourself, and return the saved value.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • +1 for a specific example - I think this is very close to what I'm looking for - if `i` were somehow destroyed after the postfix call, but before the `printf` call, the example would definitely illustrate the issue. – kfmfe04 Feb 12 '13 at 05:01
3

Sure it was good reason for that. Post increment operator does following things:

  1. increments variable and
  2. returns its old value.

There is no way of returning the reference to the 'old value' of variable. It's gone.

y0prst
  • 611
  • 1
  • 6
  • 13
  • +1 I think this may be the best explanation yet: without some kind of *lazy or postponed evaluation*, there seems to be no way to return-by-reference. – kfmfe04 Feb 12 '13 at 05:07