-1

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?

mada
  • 1,646
  • 1
  • 15
  • You can have a const reference to a non-const object. What you can't have is the opposite, i.e a non-const reference to a const object. – NathanOliver Aug 30 '22 at 20:17
  • You asked *So how r1, which is of type const int&&, is now binding to an xvalue of type int?* Doesn't that answer what you asked? – NathanOliver Aug 30 '22 at 20:21

1 Answers1

2

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.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • "_The concept of reference-compatibility was invented in order to describe what types of objects a reference can refer to._" So do you mean that if `cv1 T1` is reference-compatible with `cv2 T2`, then a reference to `cv1 T1` can bind to an object of type `cv2 T2`? – mada Aug 31 '22 at 08:47
  • In the first example, does this wording is correct `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` I mean does this what's actually happening? – mada Aug 31 '22 at 08:49
  • @H.K. yes to both. – Brian Bi Aug 31 '22 at 11:36
  • Thanks. But are you see there's a redundancy here? I mean `T4` is adjusted to `cv1 T4` using [conv.qual]; then before applying [conv.rval] `cv1 T4` becomes again `T4`; So, I see that [conv.qual] is unnecessary here because note, `T4` is adjusted using [conv.qual] to `cv1 T4` then the adjustments are ignored per [expr.type]/2. So why do we need to do so? – mada Aug 31 '22 at 11:50
  • @H.K. Comments should be used to seek clarification about answers, not to raise additional lines of inquiry that weren't present in the original question. – Brian Bi Aug 31 '22 at 12:50