Title
Double temporaries in reference initialization
Status
c++14
Section
9.4.4 [dcl.init.ref]
Submitter
Nikolay Ivchenkov

Created on 2013-01-10.00:00:00 last changed 114 months ago

Messages

Date: 2014-02-15.00:00:00

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

Date: 2013-09-15.00:00:00

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:

    • Otherwise:

      • If T1 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]); 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.

      • If T1 is a non-class type, a temporary of type “cv1 T1” is created and copy-initialized (9.4 [dcl.init]) 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.

      If T1 is reference-related to T2,:

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

      • If T1 is reference-related to T2 and if the reference is an rvalue reference, the initializer expression shall not be an lvalue.

    [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
      ...
    
  2. Change 12.2.2.5 [over.match.copy] paragraph 1 as follows:

  3. 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
Date: 2013-10-14.00:00:00
N3690 comment CA 30

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?

History
Date User Action Args
2014-11-24 00:00:00adminsetstatus: dr -> c++14
2014-03-03 00:00:00adminsetmessages: + msg4952
2014-03-03 00:00:00adminsetstatus: ready -> dr
2013-10-14 00:00:00adminsetmessages: + msg4593
2013-10-14 00:00:00adminsetstatus: drafting -> ready
2013-05-03 00:00:00adminsetstatus: open -> drafting
2013-01-10 00:00:00admincreate