cv-qualification for indirect reference binding via conversion function
9.4.4 [dcl.init.ref]
Michael Wong

Created on 2012-02-06.00:00:00 last changed 49 months ago


Date: 2014-11-15.00:00:00

[Moved to DR at the November, 2014 meeting.]

Date: 2014-02-15.00:00:00

Proposed resolution (February, 2014):

Change 9.4.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:

  • If the reference is an lvalue reference...

  • Otherwise, the reference shall be an lvalue reference to a non-volatile const type...

    • If the initializer expression

      • is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or

      • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an xvalue, class prvalue, or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see [over.match.ref]),

      then the reference is bound to the value of the initializer expression in the first case and to the result of the conversion in the second case (or, in either case, to an appropriate base class subobject). In the second case, if the reference is an rvalue reference and the second standard conversion sequence of the user-defined conversion sequence includes an lvalue-to-rvalue conversion, the program is ill-formed. [Example:

        struct A { };
        struct B : A { } b;
        extern B f();
        const A& rca2 = f();                // bound to the A subobject of the B rvalue.
        A&& 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 i2 = 42;
        int&& rri = static_cast<int&&>(i2); // bound directly to i2
        B&& rrb = x;                        // bound directly to the result of operator B
        int&& rri2 = X();                   // error: lvalue-to-rvalue conversion applied to the
                                            // result of operator int&

      end example]

  • Otherwise:

    • If T1 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion (9.4 [dcl.init], [over.match.copy], [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference. The program is ill-formed if the direct-initialization does not result in a direct binding or if it involves a user-defined conversion. For this direct-initialization, user-defined conversions are not considered.

    • If T1 is a non-class type Otherwise, a temporary of type “cv1 T1” is created and copy-initialized (9.4 [dcl.init]) from the initializer expression. The reference is then bound to the temporary.

    If T1 is reference-related to T2:

    • cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2; and

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


      struct Banana { };
      struct Enigma { operator const Banana(); };
      struct Alaska { operator Banana&(); };
      void enigmatic() {
        typedef const Banana ConstBanana;
        Banana &&banana1 = ConstBanana(); // ill-formed
        Banana &&banana2 = Enigma();      // ill-formed
        Banana &&banana2 = Alaska();      // ill-formed
      const double& rcd2 = 2;  // rcd2 refers to temporary with value 2.0
      double&& rrd = 2;        // rrd refers to temporary with value 2.0
      const volatile int cvi = 1;
      const int& r2 = cvi;     // error: type qualifiers dropped
      struct A { operator volatile int&(); } a;
      const int& r3 = a;       // error: type qualifiers dropped from result of conversion function
      double d2 = 1.0;
      double&& rrd2 = d2;      // error: copying initializer is lvalue of related type
      struct X { operator int&(); };
      int && rri2 = X();       // error: result of conversion function is lvalue of related type
      int i3 = 2;
      double&& rrd3 = i3;      // rrd3 refers to temporary with value 2.0

    end example]

This resolution also resolves issue 1572.

Date: 2013-04-15.00:00:00

Notes from the April, 2013 meeting:

CWG felt that the declaration of ir2a should also be an error.

Date: 2012-02-06.00:00:00

In the case of indirect reference binding, 9.4.4 [dcl.init.ref] paragraph 5 only requires that the cv-qualification of the referred-to type be the same or greater than that of the initializer expression when the types are reference-related. This leads to the following anomaly:

  class A {
    operator volatile int &();
  A a;

  const int & ir1a = a.operator volatile int&(); // error!
  const int & ir2a = a; // allowed! ir = a.operator volatile int&();

Is this intended?

Date User Action Args
2017-02-06 00:00:00adminsetstatus: drwp -> cd4
2015-05-25 00:00:00adminsetstatus: dr -> drwp
2015-04-13 00:00:00adminsetmessages: + msg5384
2014-11-24 00:00:00adminsetstatus: ready -> dr
2014-03-03 00:00:00adminsetmessages: + msg4834
2014-03-03 00:00:00adminsetstatus: drafting -> ready
2013-05-03 00:00:00adminsetmessages: + msg4336
2013-05-03 00:00:00adminsetstatus: open -> drafting
2012-02-06 00:00:00admincreate