Title
List-initialization of aggregate from same-type object
Status
cd4
Section
9.4.5 [dcl.init.list]
Submitter
Jason Merrill

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

Messages

Date: 2014-11-15.00:00:00

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

Date: 2014-06-15.00:00:00

Proposed resolution (June, 2014):

  1. Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

  2. List-initialization of an object or reference of type T is defined as follows:

    • If T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).

    • Otherwise, if T is a character array and the initializer list has a single element that is an appropriately-typed string literal (9.4.3 [dcl.init.string]), initialization is performed as described in that section.

    • If Otherwise, if T is an aggregate,

    • Otherwise, if the initializer list has no elements...

    • Otherwise, if T is a specialization of std::initializer_list<E>...

    • Otherwise, if T is a class type...

    • Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization); if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed. [Example:...

    • Otherwise...

  3. Change 12.2.2.8 [over.match.list] paragraph 1 as follows:

  4. When objects of non-aggregate class type T are list-initialized (9.4.5 [dcl.init.list]) such that 9.4.5 [dcl.init.list] specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor...
  5. Change 12.2.4.2 [over.best.ics] paragraph 4 as follows:

  6. ...and the constructor or user-defined conversion function is a candidate by

    • 12.2.2.4 [over.match.ctor], when the argument is the temporary in the second step of a class copy-initialization, or

    • 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] (in all cases), or

    • the second phase of 12.2.2.8 [over.match.list] when the initializer list has exactly one element, and the target is the first parameter of a constructor of class X, and the conversion is to X or reference to (possibly cv-qualified) X,

    user-defined conversion sequences are not considered.

  7. Change 12.2.4.2.6 [over.ics.list] paragraphs 1-2 as follows, moving the footnote from paragraph 3:

  8. When an argument is an initializer list (9.4.5 [dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.

    If the parameter type is a class X and the initializer list has a single element of type cv U, where U is X or a class derived from X, the implicit conversion sequence is the one required to convert the element to the parameter type.

    Otherwise, if the parameter type is a character array [Footnote: Since there are no parameters of array type, this will only occur as the underlying type of a reference parameter. —end footnote] and the initializer list has a single element that is an appropriately-typed string literal (9.4.3 [dcl.init.string]), the implicit conversion sequence is the identity conversion.

    If Otherwise, if the parameter type is std::initializer_list<X> and...

  9. Change 12.2.4.2.6 [over.ics.list] paragraph 7 as follows:

  10. Otherwise, if the parameter type is not a class:

    • if the initializer list has one element that is not itself an initializer list, the implicit conversion sequence is the one required to convert the element to the parameter type; [Example:...

  11. Move the final bullet of 12.2.4.3 [over.ics.rank] paragraph 3 to the beginning of the list and change it as follows:

    • List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if

      • L1 converts to std::initializer_list<X> for some X and L2 does not, or, if not that,

      • L1 converts to type “array of N1 T”, L2 converts to type “array of N2 T”, and N1 is smaller than N2.,

      even if one of the other rules in this paragraph would otherwise apply. [Example:

        void f1(int);                                 // #1
        void f1(std::initializer_list<long>);         // #2
        void g1() { f1({42}); }                       // chooses #2
      
        void f2(std::pair<const char*, const char*>); // #3
        void f2(std::initializer_list<std::string>);  // #4
        void g2() { f2({"foo","bar"}); }              // chooses #4
      

      end example]

This resolution also resolves issues 1490, 1589, 1631, 1756, and 1758.

Date: 2014-02-15.00:00:00

Notes from the February, 2014 meeting:

The resolution above does not adequately address the related issue 1758. It appears that conversion functions and constructors must be handled separately.

Date: 2014-02-15.00:00:00

Proposed resolution (February, 2014) [SUPERSEDED]:

  1. Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

  2. List-initialization of an object or reference of type T is defined as follows:

    • If T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).

    • Otherwise, if T is a character array and the initializer list has a single element that is an appropriately typed string literal (9.4.3 [dcl.init.string]), initialization is done as described in that section.

    • If Otherwise, if T is an aggregate...

  3. Delete the final bullet of 12.2.4.2 [over.best.ics] paragraph 4, as follows:

  4. However, if the target is

    • the first parameter of a constructor or

    • the implicit object parameter of a user-defined conversion function

    and the constructor or user-defined conversion function is a candidate by

    • 12.2.2.4 [over.match.ctor], when the argument is the temporary in the second step of a class copy-initialization, or

    • 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] (in all cases), or

    • the second phase of 12.2.2.8 [over.match.list] when the initializer list has exactly one element, and the target is the first parameter of a constructor of class X, and the conversion is to X or reference to (possibly cv-qualified) X,

    user-defined conversion sequences are not considered. [Note:...

  5. Insert the following two paragraphs between 12.2.4.2.6 [over.ics.list] paragraphs 1 and 2, moving the footnote from the current paragraph 3 to the second inserted paragraph:

  6. When an argument is an initializer list (9.4.5 [dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.

    If the parameter type is a class C and the initializer list has a single element of type cv U, where U is C or a class derived from C, the implicit conversion sequence is the one required to convert the element to the parameter type.

    Otherwise, if the parameter type is a character array [Footnote: Since there are no parameters of array type, this will only occur as the underlying type of a reference parameter. —end footnote] and the initializer list has a single element that is an appropriately typed string literal (9.4.3 [dcl.init.string]), the implicit conversion is the identity conversion.

    If Otherwise, if the parameter type is std::initializer_list<X> and...

    Otherwise, if the parameter type is “array of N X[Footnote: ... —end footnote], if the initializer list has...

  7. Change 12.2.4.2.6 [over.ics.list] paragraph 7 as follows:

  8. Otherwise, if the parameter type is not a class:

    • if the initializer list has one element that is not itself an initializer list, the implicit conversion sequence is the one required to convert the element to the parameter type; [Example:...

    • ...

  9. Change 12.2.4.3 [over.ics.rank] paragraph 3 as follows:

  10. Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:

    • ...

    • List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if

      • L1 converts to std::initializer_list<X> for some X and L2 does not, or, if not that,

      • L1 converts to type “array of N1 T”, L2 converts to type “array of N2 T”, and N1 is smaller than N2.,

      even if one of the above rules would otherwise apply. [Example:

        void f1(int);                                 // #1
        void f1(std::initializer_list<long>);         // #2
        void g1() { f1({42}); }                       // chooses #2
      
        void f2(std::pair<const char*, const char*>); // #3
        void f2(std::initializer_list<std::string>);  // #4
        void g2() { f2({"foo","bar"}); }              // chooses #4
      

      end example]

This resolution also resolves issues 1490, 1589, and 1631.

Date: 2012-09-15.00:00:00

Additional notes (September, 2012):

(See messages 22368, 22371 through 22373, 22388, and 22494.)

It appears that 12.2.4.2.6 [over.ics.list] will also need to be updated in parallel with this change. Alternatively, it may be better to change 9.4.2 [dcl.init.aggr] instead of 9.4.5 [dcl.init.list] and 12.2.4.2.6 [over.ics.list].

In a related note, given

  struct NonAggregate {
    NonAggregate() {}
  };

  struct WantsIt {
    WantsIt(NonAggregate);
  };

  void f(NonAggregate n);
  void f(WantsIt);

  int main() {
    NonAggregate n;
    // ambiguous!
    f({n});
  }

12.2.4.2.6 [over.ics.list] paragraph 3 says that the call to f(NonAggregate) is a user-defined conversion, the same as the call to f(WantsIt) and thus ambiguous. Also,

    NonAggregate n;
    // #1 (n -> NonAggregate = Identity conversion)
    NonAggregate m{n};
    // #2 ({n} -> NonAggregate = User-defined conversion}
    // (copy-ctor not considered according to 12.2.4.2 [over.best.ics] paragraph 4)
    NonAggregate m{{n}};

Finally, the suggested resolution simply says “initialized from,” without specifying whether that means direct initialization or copy initialization. It should be explicit about which is intended, e.g., if it reflects the kind of list-initialization being done.

Date: 2022-02-18.07:47:23

Suggested resolution:

Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

List-initialization of an object or reference of type T is defined as follows:

  • If T is a class type and the initializer list has a single element of type cv T or a class type derived from T, the object is initialized from that element.

  • If Otherwise, if T is an aggregate...

Date: 2022-02-18.07:47:23

The current list-initialization rules do not provide for list-initialization of an aggregate from an object of the same type:

  struct X {
    X() = default;
    X(const X&) = default;
  #ifdef OK
    X(int) { }
  #endif
  };

  X x;
  X x2{x}; // error, {x} is not a valid aggregate initializer for X
History
Date User Action Args
2022-02-18 07:47:23adminsetmessages: + msg6706
2017-02-06 00:00:00adminsetstatus: drwp -> cd4
2015-05-25 00:00:00adminsetstatus: dr -> drwp
2015-04-13 00:00:00adminsetmessages: + msg5386
2014-11-24 00:00:00adminsetstatus: ready -> dr
2014-07-07 00:00:00adminsetmessages: + msg5070
2014-07-07 00:00:00adminsetstatus: drafting -> ready
2014-03-03 00:00:00adminsetmessages: + msg4886
2014-03-03 00:00:00adminsetmessages: + msg4885
2012-11-03 00:00:00adminsetstatus: open -> drafting
2012-09-24 00:00:00adminsetmessages: + msg3955
2012-02-06 00:00:00admincreate