Search code examples
c++reference-binding

Binding const rvalues reference to non-const rvalues?


The following quotes are needed in the question:

[dcl.init.ref]/5:

5- A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • (5.1) [..]
  • (5.2) [..]
  • (5.3) Otherwise, if the initializer expression
    • (5.3.1) is an rvalue (but not a bit-field) or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2, or
    • (5.3.2) [..]

then the value of the initializer expression in the first case and the result of the conversion in the second case is called the converted initializer. If the converted initializer is a prvalue, its type T4 is adjusted to type “cv1 T4” ([conv.qual]) and the temporary materialization conversion ([conv.rval]) is applied. In any case, the reference is bound to the resulting glvalue (or to an appropriate base class subobject).

(emphasis mine)

[expr.type]/2:

If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.


Consider the following example:

const int&& r1 = 0;

Taking cv1 T1 as const int, and cv2 T2 as int

It's clear that bullet [dcl.init.ref]/(5.3.1) is applied here. The initializer expression is an rvalue (prvalue); and cv1 T1 (const int) is reference-compatible with cv2 T2 (int). And since that the converted initializer is prvalue, its type T4 (int) is adjusted to cv1 T4 (const int). Then, temporary materialization is applied.

But, per [expr.type]/2, before applying temporary materialization conversion, cv1 T4 (const int) becomes int again. Then, by applying temporary materialization, we've got an xvalue denoting an object of type int. Then the reference is bound to the resulting glvalue.

Here's my first question. The reference r1 is reference to const int and the resulting glvalue is an xvalue denoting an object of type int. So how r1, which is of type const int&&, is now binding to an xvalue of type int? Is this valid binding? Is any missing wording? Am I misunderstood/missed something?


Consider another last example:

const int&& r2 = static_cast<int&&>(0);

The same wording as above applies: The initializer expression is an rvalue (xvalue) and cv1 T1 (const int) is reference-compatible with cv2 T2 (int). And since that the converted initializer is an xvalue not prvalue, [conv.qual] or even [conv.rval] is not applied (i.e, the condition "If the converted initializer is a prvalue, ..") isn't satisfied.

I know that [conv.rval] isn't needed here since the initializer expression is already an xvalue, but [conv.qual] is required.

And that's my last question. The reference r2 is reference to const int and the resulting glvalue is an xvalue denoting an object of type int. So how r2, which is of type const int&&, is now binding to an xvalue of type int? Is this valid binding? Is any missing wording? Am I misunderstood/missed something?


Solution

  • Here's my first question. The reference r1 is reference to const int and the resulting glvalue is an xvalue denoting an object of type int. So how r1, which is of type const int&&, is now binding to an xvalue of type int? Is this valid binding?

    Yes, that's what happens. I fail to see the issue here.

    I know that [conv.rval] isn't needed here since the initializer expression is already an xvalue, but [conv.qual] is required.

    No it isn't. Again, I fail to see the issue.

    There is, in fact, no rule that says that a reference of type T&&, can only refer to an object whose type is exactly T. A const int&& can refer to an int object. The concept of reference-compatibility was invented in order to describe what types of objects a reference can refer to.