Why is ++x a lvalue and x++ a rvalue? [duplicate]

So I've been reading up on lvalue and rvalues and I'm a bit confused about the difference between ++x and x++ when it comes to this categorization.

Why is ++x a lvalue and x++ a rvalue?


++x returns a reference to the object you incremented, where as x++ returns a temporary copy of x's old value.

At least this would be the "normal" way by to implement these operators by convention. And all built-in types work this way. And if you've read about lvalues / rvalues then you would see that since the prefix operator returns the named object itself it would be an lvalue, where as the postfix operator returns a copy of a local temporary, which would then qualify as an rvalue.

Note: Also, we have prvalues, xvalues and such now, so it's technically a bit more complicated these days. Look here for more info.


C++ (as opposed to C) is a devoted lvalue-preserving language: it strives to painstakingly preserve the "lvalueness" of an expression whenever it is possible.

  • It is very easy to preserve the "lvalueness" of pre-increment: just increment the operand and return it as an lvalue. Done. The returned lvalue will contain exactly the result it is supposed to contain: the new (incremented) value of the operand.

  • And at the same time it is virtually impossible to preserve "lvalueness" of post-increment: by definition, the result of post-increment is the old (original) value of the operand. If you attempt to return an lvalue from post-increment, you will have to somehow simultaneously ensure two things: 1) the lvalue is incremented, 2) the calling code sees the old value when it looks into that same lvalue (!). This combination of requirements is so contradictory that is basically impossible to implement in C++ object model.

    In order to implement the proper post-increment behavior one has to make sure that the calling code does not look directly into the operand, but rather looks into some conceptual or physical "proxy" that makes the calling code to "see" the old value of the operand. That proxy might be a temporary object that holds the old value. Or that proxy might be something that generates the old value on the fly by subtracting 1 from the new value. In any case, that proxy is what prevents the calling code from accessing the original lvalue.

This is why C++ takes advantage of the easily achievable opportunity to preserve "lvalueness" of pre-increment, but concedes to the impossibility to achieve the same with post-increment. In case of post-increment it is just not worth the effort to deviate from classic standard C behavior, which tends to discard "lvalueness" quickly and happily.


Maybe writing down the workings in pseudo-code of the operators for int makes it more clear:

prefix:

int& Prefix(int& val)
{
  int& value = val;
  value += 1;
  return value;
}

postfix:

int Postfix(int& val)
{
  int oldValue = val;
  val += 1;
  return oldValue; 
}

The difference is in what is returned by the two operators, one by value and one by reference.


Why is ++x a lvalue

I am guessing it is an lvalue because it can be.

++x it can be thought of as

((x = x + 1), x)  // Increment x. Then evaluate to the value of x

or

((x += 1), x)    //  Increment x. Then evaluate to the value of x

It makes sense to evaluate ++x to an lvalue.

x++ a rvalue?

Because it can't be an lvalue.

x++ it can be thought of as

((unnamed_variable = x), (x = x + 1), unnamed_variable)

or

((unnamed_variable = x), (x += 1), unnamed_variable)

x++ evaluates to the value of before x is incremented. Since there is no variable that can store that value, it cannot be an lvalue.


For built-in types, such as int, x++ yields the old value of x. There is no storage associated with this, so it would be impossible for the expression to be an lvalue.

In C, ++x yielded the new value of x (and was not an lvalue). Since the object x actually contains that same value, it is possible to have ++x designate the object x (i.e. be an lvalue). This is added functionality compared to yielding an rvalue, and the designer of C++ decided that this would be an improvement to the language.

For class types, it is possible overload any operator in such a way that use of the operator can yield an lvalue, xvalue, or prvalue. However it is considered good style to make overloaded operators have similar semantics to built-in operators, which is why it is normal for people to overload ++x to yield an lvalue and to overload x++ to yield an rvalue.