Search code examples
c++c++11lifetimexvalueprvalue

Some differences between xvalue and prvalue


I've been carefully studying C++ catogories recently. The difference between lvalue and rvalue seems to be clear, but I got confused when it comes to prvalue and xvalue.
Given the example below:

#include <iostream>
using std::cout;
using std::endl;
using std::move;
class Type {
public:
    int value;
    Type(const int &value=0) :value(value) {}
    Type(const Type &type) :value(type.value){}
    Type(Type &&type) noexcept :value(type.value) {}
    Type &operator= (const Type &type) {
        value = type.value;
        return *this;
    }
    Type &operator=(Type &&type) noexcept{
        value = type.value;
        return *this;
    }
};
Type foo1(const Type &value) {
    return Type(value);
}
Type &&foo2(const Type &value) {
    return Type(value);
}
int main() {
    Type bar1(123);
    cout << foo1(bar1).value << endl;
    cout << foo2(bar1).value << endl;
    Type bar2;
    bar2 = foo1(bar1);
    cout << bar2.value << endl;
    bar2 = foo2(bar1);
    cout << bar2.value << endl;
    return 0;
}

Running the example, the console puts:
123
123
123
-858993460
Can anyone explan why it gives an unexpected value in the last output?
What feature does this example show about xvalue?


Solution

  • foo2 is returning reference bound to temporary which is destroyed immediately; it always returns a dangled reference.

    a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such function always returns a dangling reference.

    Dereference on the returned reference like foo2(bar1).value and bar2 = foo2(bar1); leads to UB; anything is possible.

    On the other hand, foo1 doesn't have such issue. The return value is moved from the temporary object.