This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 120a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2026-05-31


1138. Rvalue-ness check for rvalue reference binding is wrong

Section: 9.5.4  [dcl.init.ref]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 48
N3092 comment GB 37
N3092 comment DE 10

The requirement that an rvalue reference must be bound to an rvalue is found in 9.5.4 [dcl.init.ref] bullet 5.2:

This is not quite correct, as it is phrased in terms of the value category of the initializer expression itself rather than that of the result of any conversions applied to the initializer. It should be permitted to bind an rvalue reference to a temporary created from an lvalue, for instance, or to the rvalue result of a conversion function for an lvalue object of class type. Also, it should not be permitted to bind an rvalue reference to the lvalue result of a conversion function for a class rvalue.

Proposed resolution (August, 2010):

Change 9.5.4 [dcl.init.ref] paragraph 5 as follows:

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

  double d = 2.0;
  double& rd = d;          // rd refers to d
  const double& rcd = d;   // rcd refers to d
  struct A { };
  struct B : A { operator int&(); } b;
  A& ra = b;               // ra refers to A subobject in b
  const A& rca = b;        // rca refers to A subobject in b
  int& ir = B();           // ir refers to the result of B::operator int&

end example]

  • Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference and the initializer expression shall be an rvalue or have a function type. [Example:

  •   double& rd2 = 2.0;       // error: not an lvalue and reference not const
      int  i = 2;
      double& rd3 = i;         // error: type mismatch and reference not const
      double&& rd4 = i;        // error: rvalue reference cannot bind to lvalue
    

    end example]

      struct A { };
      struct B : A { } b;
      extern B f();
      const A& rca = f();      // bound to the A subobject of the B rvalue.
      A&& rcb rra = f();       // same as above
      struct X {
      operator B();
      operator int&();
      } x;
      const A& r = x;                     // bound to the A subobject of the result of the conversion
      int&& rri = static_cast<int&&>(i);  // bound directly to i
      B&& rrb = x;                        // bound directly to the result of operator B
      int&& rri2 = X();                   // error: lvalue-to-rvalue conversion applied to result of operator int&
    

    end example]

  • If the initializer expression is an rvalue, with T2 an array type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound to the object represented by the rvalue (see 7.2.1 [basic.lval]).

  • Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (9.5 [dcl.init]). The reference is then bound to the temporary. If T1 is reference-related to T2, cv1 must shall be the same cv-qualification as, or greater cv-qualification than, cv2; otherwise, the program is ill-formed. If T1 is reference-related to T2 and the reference is an rvalue reference, the initializer expression shall not be an lvalue. [Example:

  •   const double& rcd2 = 2;     // rcd2 refers to temporary with value 2.0
      double&& rcd3 rrd = 2;      // rcd3 rrd refers to temporary with value 2.0
      const volatile int cvi = 1;
      const int& r = cvi;         // error: type qualifiers dropped
      double&& rrd2 = d;          // error: copying lvalue of related type
      double&& rrd3 = i;          // rrd3 refers to temporary with value 2.0
    

    end example]

    In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.

    This resolution also resolves issue 1139.