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

2023-01-15


1604. Double temporaries in reference initialization

Section: 9.4.4  [dcl.init.ref]     Status: C++14     Submitter: Nikolay Ivchenkov     Date: 2013-01-10

N3690 comment CA 30

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

Bullet 2 sub-bullet 2 of 9.4.4 [dcl.init.ref] paragraph 5 says,

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.4 [dcl.init]). The reference is then bound to the temporary.

It is not clear what “using the rules for a non-reference copy-initialization” means. If it means that the temporary is initialized as if it were a standalone variable, the last bullet of 9.4 [dcl.init] paragraph 17 could apply:

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 12.2.2.5 [over.match.copy], and the best one is chosen through overload resolution (12.2 [over.match]). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.

That is, for an example like

  struct X {
    X(int) {}
      X(X const &) = delete;
    };

  void f() {
    X const &x = 0;
  }

the specification requires creation of a temporary X(0), copying that tempoary to another temporary (subject to copy elision), and binding the reference to that second temporary. In the example above, because the copy constructor is deleted, the example is ill-formed, although current implementations fail to diagnose it as an error.

Is this intended?

Proposed resolution (September, 2013):

  1. Change the last bullet of 9.4.4 [dcl.init.ref] paragraph 5, breaking it into sub-bullets, and the subsequent example as follows:

  2. [Example:

      struct Banana { };
      struct Enigma { operator const Banana(); };
      void enigmatic() {
        typedef const Banana ConstBanana;
        Banana &&banana1 = ConstBanana(); // ill-formed
        Banana &&banana2 = Enigma();      // ill-formed
      }
    
      const double& rcd2 = 2;             // rcd2 refers to temporary with value 2.0
      ...
    
  3. Change 12.2.2.5 [over.match.copy] paragraph 1 as follows:

  4. Under the conditions specified in 9.4 [dcl.init], as part of a copy-initialization of an object of class type, a user-defined conversion can be invoked to convert an initializer expression to the type of the object being initialized. Overload resolution is used to select the user-defined conversion to be invoked. [Note: The conversion performed for indirect binding to a reference to a possibly cv-qualified class type is determined in terms of a corresponding non-reference copy-initialization. —end note] Assuming that