c++c++11c++14c++17stdinitializerlist

Pack expansion of variadic list of types into initializer list of complex types - is it legal?


I would like to "materialize" a variadic types list into an initializer_list of related values. For example, having an std::tuple of several std::integral_constant<T, x> get an std::initializer_list<T>{...}. In general case, I would like to get initializer_list of some complex type, like std::string.

But the following simple example gives me a crash when compiled by Clang (although it works with GCC, at least on Coliru), so I suspect UB (or bug in Clang):

template <class... Ts>
std::initializer_list<const std::string> materialize()
{
    return {
      std::to_string(Ts::value)...
    };
}

void print_out()
{
   for (const auto & x : materialize<std::true_type, std::false_type>()) {
      std::cout << x << "\n";
   }
}

Live on Coliru

So, is such code legal? In C++11/14/17?


Solution

  • Two things about initializer_list:

    Initializer lists may be implemented as a pair of pointers or pointer and length. Copying a std::initializer_list does not copy the underlying objects.

    and

    The underlying array is not guaranteed to exist after the lifetime of the original initializer list object has ended. The storage for std::initializer_list is unspecified (i.e. it could be automatic, temporary, or static read-only memory, depending on the situation).

    so in this line

    return {
          std::to_string(Ts::value)...
        };
    

    you are creating local array, initializer_list keeps pointer to the beginning / end of this array, when function goes out of scope you have dangling pointers.