Search code examples
c++pointersreferencervalue-referencetemporary-objects

Why r-value reference to pointer to const initialized with pointer to non-const doesn't create an temporary and bind it with it?


If we want to initialize an reference with an different type, we need to make it const (const type*) so that an temporary can be generated implicit and the reference binded to with. Alternativaly, we can use r-value references and achieve the same [1]:

Rvalue references can be used to extend the lifetimes of temporary objects (note, lvalue references to const can extend the lifetimes of temporary objects too, but they are not modifiable through them) :

[...]

Samples

Case 1

double x = 10;

int &ref = x; //compiler error (expected)

Case 2

double x = 10;
const int &ref = x; //ok

Case 3

double x = 10;
int &&ref = x; //ok

If we try to do the same with reference to const pointer (const type* &) and initialize it with non-const pointer (type*), different than I expected, only the case 2 works. Why the case 3 leads to compiler error? Why the temporary isn't generated?

Case 1

int x = 10;

int *pX = &x;

const int* &ref = pX; //compiler error (expected)

Case 2

int x = 10;
int *pX = &x;
const int* const &ref = pX; //ok (expected)

Case 3

int x = 10;
int *pX = &x;
const int* &&ref = pX; //compiler error (why?)

In gcc 12.1.0 and clang 14.0.4 with flag -std=c++20 (and some others) the case 3 above don't compile.

  • gcc : 'error: cannot bind rvalue reference of type 'const int*&&' to lvalue of type 'int*''
  • clang: 'error: rvalue reference to type 'const int *' cannot bind to lvalue of type 'int *'

Why in the case of int&, int&&, etc., all worked well, and in this case with pointer there was compiler error? Is there some imprecision in my current knowledge? (I'm novice)

If we do the same with an pr-value (int*), everything works well

Case 3

int x = 10;
//int *pX = &x;
const int* &&ref = &x; //ok (why?)

Related questions:

<Non-const reference to a non-const pointer pointing to the const object>

<const reference to a pointer not behaving as expected>

  • Similar questions but both suggest using reference to const (type* const &). I wonder why r-value reference dont works with pointer, but work with int, etc., and that wasn't asked.

<What does T&& (double ampersand) mean in C++11?>

  • r-value reference (&&)

References

[1] https://en.cppreference.com/w/cpp/language/reference


Solution

  • The standard has a concept of two types being reference-related. This is fulfilled by two types being similar, which basically means if they are the same type with the same number of pointers but possibly different cv-qualifiers (e.g., int and const int are similar, int* const ** volatile and volatile int** const * are similar, and crucially int* and const int* are similar).

    The standard says that ([dcl.init.ref]p(5.4.4)):

    If T1 is reference-related to T2:

    • if the reference is an rvalue reference, the initializer expression shall not be an lvalue.

    And since const int* &&ref = pX;, an rvalue reference, and T1 = const int* is reference-related to T2 = decltype(pX) = int*, this applies and so this just isn't allowed. const int* &&ref = std::move(pX); doesn't run into this issue, since the initializer is no longer an lvalue. And of course, explicitly doing the const conversion const int* &&ref = (const int*) pX; also works.

    Presumably, this is so const T x; T&& y = x; (binding y to a temporary copy of x) isn't allowed, but by a quirk of the standard it also extends to pointers.