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

2024-10-26


1290. Lifetime of the underlying array of an initializer_list member

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: James Dennett     Date: 2011-04-08

[Moved to DR at the October, 2012 meeting.]

A question has arisen over expected behavior when an initializer_list is a non-static data member of a class. Initialization of an initializer_list is defined in terms of construction from an implicitly allocated array whose lifetime "is the same as that of the initializer_list object". That would mean that the array needs to live as long as the initializer_list does, which would on the face of it appear to require the array to be stored in something like a std::unique_ptr<T[]> within the same class (if the member is initialized in this manner).

It would be surprising if that was the intent, but it would make initializer_list usable in this context.

It would also be reasonable if this behaved similarly to binding temporaries to reference members (i.e., "temporary bound to a reference member in a constructor's ctor-initializer (11.9.3 [class.base.init]) persists until the constructor exits."), though this approach would probably prevent use of an initializer_list member in that context.

Proposed resolution (February, 2012):

  1. Change 9.4.5 [dcl.init.list] paragraphs 5-6 as follows:

  2. An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated an a temporary array of N elements of type E, where...

    The lifetime of the array is the same as that of the initializer_list object. The array has the same lifetime as any other temporary object (6.7.7 [class.temporary]), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary. [Example:

      typedef std::complex<double> cmplx;
      std::vector<cmplx> v1 = { 1, 2, 3 };
    
      void f() {
        std::vector<cmplx> v2{ 1, 2, 3 };
        std::initializer_list<int> i3 = { 1, 2, 3 };
      }
    
      struct A {
        std::initializer_list<int> i4;
        A(): i4{1,2,3} { }  // creates an A with a dangling reference
      };
    

    For v1 and v2, the initializer_list object is a parameter in a function call, so the and array created for { 1, 2, 3 } have has full-expression lifetime. For i3, the initializer_list object is a variable, so the and array have automatic persists for the lifetime of the variable. For i4, the initializer_list object is initialized in a constructor's ctor-initializer, so the array persists only until the constructor exits, and so any use of the elements of i4 after the constructor exits produces undefined behavior. —end example] [Note: The implementation is free to allocate the array in read-only memory if an explicit array with the same initializer could be so allocated. —end note]

  3. Change 6.7.7 [class.temporary] paragraph 5 as follows:

  4. The second context is when a reference is bound to a temporary. [Footnote: The same rules apply to initialization of an initializer_list object (9.4.5 [dcl.init.list]) with its underlying temporary array. —end footnote] The temporary to which...