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.