c++c++11movervaluexvalue

xvalues: differences between non class types and class types


Consider the minimal example below:

#include<utility>

struct S { };

int main() {
    S s;
    std::move(s) = S{};
}

It compiles with no errors.
If I use non class types instead, I get an error.
As an example, the code below doesn't compile:

#include<utility>

int main() {
    int i;
    std::move(i) = 42;
}

The same happens with enums, scoped enums, and so on.
The error (from GCC) is:

using xvalue (rvalue reference) as lvalue

What's the rationale behind this?

I guess it's right, but I'd like to understand what's the reason for which I can do that with all the types but the non class ones.


Solution

  • I'm trying to reply to my own question with a bunch of links to the standard.
    I'm quite sure that I'll write something that is terribly wrong and someone will come banding words with me.
    Well, I did my best to explain how one can deduce from the standard what's described in the question.
    Feel free to downvote if needed, but please let me know what's wrong so as to be able to fix the answer and understand the error.
    Thank you.


    3.9/8 (types):

    An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not cv void.

    5.2.2/10 (expressions, function call):

    A function call is [...] an xvalue if the result type is an rvalue reference to object type

    Thus std::move is an xvalue expression in either cases.

    5.18/3 (assignment):

    If the left operand is not of class type, the expression is implicitly converted [...] to the cv-unqualified type of the left operand.

    This does not add useful information, but it's for the sake of completeness.

    4.1/2 (lvalue-to-rvalue conversion):

    Otherwise, if T has a class type, the conversion copy-initializes a temporary of type T from the glvalue and the result of the conversion is a prvalue for the temporary.

    Otherwise, the value contained in the object indicated by the glvalue is the prvalue result.

    12.2 (temporary objects) does the rest.

    So, as mentioned by @xaxxon in the comments, I was actually trying to do (let me write) 42 = 0; and it is not a valid expression in C++.

    As correctly pointed out in the comments by @bogdan, the right part of the standard to which to refer in this case is 5.18/1 (Assignment):

    All require a modifiable lvalue as their left operand [...]

    While 5/2 and 5/3 clarify that the statement applies to built-in operators only.