Search code examples
c++11xvalue

Does xvalue/prvalue category really matter?


The only way to create xvalue is use static_cast< T&& > conversion. Yes, according to standard there are also three other situation but all of them require another xvalue. So, for simplicity, I will assume that is only way for now.

We need pass something to static_cast. Let's call it «gizmo». No matter what exactly is gizmo (lvalue or rvalue), result of static_cast would be rvalue reference to exactly same thing (I mean that it would not produce any temporary object). Value category of result would be either prvalue or xvalue (for objects). Let us consider the second case.

I understand that xvalue is far better for objects than prvalue cause it has dynamic type; but strange thing is that I don't know any action with «gizmo» whose produce prvalue that contain reference to same thing: it is look like all action that convert «gizmo» to prvalue (like static_cast< T > or pass to function with non-reference result) would create temporary object (with call copy constructor and so on).

Summarize, if we have value of object type we can either:

  1. produce xvalue with reference to that value

or

  1. produce prvalue with reference to temporary object (probably copy of value)*

Maybe really important thing is where rvalue reference actually refers to (same thing or copy) rather than it xvalue/prvalue category? If so we might think that static_cast< T&& > just only way to save reference to same thing and don't care anymore about xvalue/prvalue. Is not it?


  • Of course if value is already prvalue we are not obliged to do something to get prvalue but in this case dynamic type can't be lost. So, there is no difference with xvalue again.

Solution

  • xvalue and prvalue matters when using decltype deduction rules, for example. What cppreference says:

    • a) if the value category of expression is xvalue, then decltype yields T&&;
    • b) if the value category of expression is lvalue, then decltype yields T&;
    • c) if the value category of expression is prvalue, then decltype yields T.

    In this way, the compiler has a better hint of what kind of expression is coming. And of course, it's just important in a semantic sense of dealing with expressions.

    Example:

    struct A {};
    int main() {
        decltype(auto) a1 = A(); //prvalue, type of a1 is A
        decltype(auto) a2 = std::move(A()); //xvalue, type of a2 is A&&
    }
    

    Using auto in this example, both a1 and a2 would get A as type.