c++language-lawyerrvalue-reference

Why does C++ prohibit binding T1 && to T2 lvalue when T1 is reference-related to T2 (and T1 and T2 are not class types)?


I'm exploring this question: Given char * x, why doesn't const char * && rc2 = x compile?

And I found dcl.init.ref#5.4.4 which says (when we are binding T1 && to T2 lvalue where T2 is not class type, ) and if T1 is reference-related to T2, then if the reference is an rvalue reference, the initializer expression shall not be an lvalue.

Why is there a rule like this? What does this rule want to avoid?


I clearly know that bindings like int i; int && ri = i; is illegal, but my question here is, I expect const char * && rc2 = static_cast<const char *>(x); where static_cast<const char *>(x) should be an prvalue which can bind to rvalue references.

As int i; double && rd = i; is valid, why is char * x; const char * && rc2 = x; invalid?


Solution

  • This rule covers not only cases such as

    int x;
    int&& r = x;
    

    but also

    const int x = 0;
    int&& r = x;
    

    Here, we don't have an exact match in types: the reference wants to bind to an int, but the initializer expression has type const int. Just like how we don't want the first example to create a temporary int object (a copy of x) and then bind r to that, in the second example we also don't want a temporary int object to be created by copying x.

    Or, similarly

    struct Base { /* ... */ };
    struct Derived : Base { /* ... */ };
    Derived d;
    Base&& b = d;
    

    The idea is that if you have a reference binding that could have been a direct binding if only the reference were of the appropriate kind (i.e. an lvalue reference instead of an rvalue reference) and had the appropriate cv-qualification, then it's probably the programmer's mistake. Creating a temporary object is usually not the desired behavior.

    Reference-relatedness captures this notion of "could have been a direct binding".

    As for this case that you mentioned:

    char * x; const char * && rc2 = x;
    

    the same logic applies: the reference-relatedness tells you that this is "too close" to a valid reference binding. If you changed the type of the reference to const char* const&, then the binding would be valid.