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

2025-03-08


2985. Unclear rules for reference initialization with conversion

Section: 9.4.4  [dcl.init.ref]     Status: tentatively ready     Submitter: Anoop Rana     Date: 2024-08-13

(From submission #596.)

It is unclear whether 9.4.4 [dcl.init.ref] bullet 5.4.1 intends list-initialization or not-list-initializaiton when it talks about direct-initialization.

Furthermore, the following example was well-formed before the resolution of issue 1604 was applied:

  struct X { };
  
  struct Y : X {};

  struct Z {
    operator const Y () const;
  };
  
  Z z;
  X&& r = z; // #1, ill-formed; was well-formed before CWG1604

Possible resolution (January, 2025) [SUPERSEDED]:

Change in 9.4.4 [dcl.init.ref] bullet 5.4.1 as follows:

If T1 or T2 is a class type, 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], 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result E of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference using the form ( E ); if E is a prvalue, its cv-qualification is adjusted to cv1. For this direct-initialization, user-defined conversions are not considered.

Additional notes (February, 2025)

Permitting a binding of X&& to a const Y seems ill-advised; the change effected by issue 1604 in that regard is intended.

In more detail, copy-initializing a T object from a cv T prvalue succeeds:

  struct X { X() = default; X(X&&) = delete; };
  using CX = const X;
  X x = CX();    // OK, default-initializes x

However, even with guaranteed copy elision, the pre-CWG1604 model does not handle derived classes appropriately when reference binding:

  struct X {
    X() = default;
    X(X&&) = delete;
  };
  struct Y : X {};
  struct Z {
    operator Y() { return Y(); }
  };
  X&& x = Z();

In this case, the rvalue reference x should bind directly to the Y materialized prvalue; there should never be an attempt to copy-initialize an X from Z to satisfy the reference binding. However, such direct reference binding would not be expected to work for a const Y prvalue.

Proposed resolution (February, 2025):

Change in 9.4.4 [dcl.init.ref] bullet 5.4.1 as follows:

If T1 or T2 is a class type, 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], 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result E of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference using the form ( E ). For this direct-initialization, user-defined conversions are not considered.